Skip to content

module property with a free-function getter and member setter crashes at module load #1519

Open

Description

@kevinushey

class_<T>::property() has one overload per getter/setter combination (member function, const member function, or free function taking T*). The overload taking a free-function getter and a member-function setter never returns *this:

template <typename PROP>
self& property( const char* name_, PROP (*GetMethod)(Class*), void (Class::*SetMethod)(PROP), const char* docstring = 0 ){
AddProperty(
name_,
new CppProperty_GetPointer_SetMethod<Class,PROP>(GetMethod, SetMethod, docstring)
) ;
}

template <typename PROP>
self& property( const char* name_, PROP (*GetMethod)(Class*), void (Class::*SetMethod)(PROP), const char* docstring = 0 ){
    AddProperty(
        name_,
        new CppProperty_GetPointer_SetMethod<Class,PROP>(GetMethod, SetMethod, docstring)
    ) ;
}

Flowing off the end of a non-void function is undefined behavior, and the result is used by the next call in the class_<T> builder chain. With Apple clang the compiler emits a trap instruction there, so any module that uses this overload kills the R session as soon as the module is loaded. The compiler does warn:

inst/include/Rcpp/module/Module_Add_Property.h:86:2: warning: non-void function does not return a value [-Wreturn-type]

but sourceCpp() and package builds don't surface warnings by default. The code dates back to 2010; none of the free-function property() overloads are exercised by the test suite.

Reproducible example

repro.cpp:

#include <Rcpp.h>

using namespace Rcpp;

class Counter {
public:
    Counter() : n(0) {}

    void set_n(int n_) {
        n = n_;
    }

    int n;
};

// getter as a free function taking the object pointer
int get_n(Counter* c) {
    return c->n;
}

RCPP_MODULE(counter_module) {
    class_<Counter>("Counter")
        .constructor()
        // free-function getter + member-function setter
        .property("n", &get_n, &Counter::set_n);
}

repro.R:

Rcpp::sourceCpp("repro.cpp")
cat("module loaded\n")

counter <- new(Counter)
counter$n <- 5L
print(counter$n)

Output with R 4.6.1 (macOS 27, Apple clang 21, Rcpp 1.1.2.6 / current master):

Trace/BPT trap: 5          Rscript repro.R

Rscript exits with code 133 (SIGTRAP) while sourceCpp() loads the module; module loaded is never printed. Other compilers may not trap, but the behavior is still undefined.

I'll follow up with a PR that adds the missing return *this; and adds tests covering every property() overload.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions