there-is-no-rcpp-const

Note to self: Rcpp passes all its own classes by reference and ignores const

Stars
5
Committers
1

Note to Self: There is no Rcpp const!

devtools::load_all (".", export_all = FALSE)
devtools::document (".")

Rcpp passes all internals objects by reference only, never by value. This repo demonstrates the implications of this as a clear and simple Note To Self. Note that this is acknowledged behaviour of Rcpp, as clarified by this Rcpp issue, the corresponding pull request, and the resultant entry in the Rcpp FAQ, Section 5.1.

The demo here relies on these C++ functions

// [[Rcpp::export]]
void rcpp_test1(const Rcpp::DataFrame df) {
    Rcpp::IntegerVector x = df ["x"];
    x = x + 1;
}

// [[Rcpp::export]]
void rcpp_test2(const Rcpp::DataFrame df) {
    std::vector <int> x = df ["x"];
    for (auto i: x)
        i++;
}
devtools::load_all (".", export_all = FALSE)
df <- data.frame (x = 1:5, y = 1:5)
test1 (df)
df

Running test1() alters the values of df because the internal Rcpp::IntegerVector object is constructed strictly by reference only. Crucially, in doing so, Rcpp also complete ignores the const directive, as discussed in the FAQ linked above. In contrast, the rcpp_test2 function implements an implicit copy-by-value through the typecast to std::vector, and so

test2 (df)
df

An alternative is Rcpp::clone(), as demonstrated in the third function:

// [[Rcpp::export]]
void rcpp_test3(const Rcpp::DataFrame df) {
    const Rcpp::IntegerVector x = df ["x"];
    Rcpp::IntegerVector x2 = Rcpp::clone (x);
    x2 = x2 + 1;
}

This also leaves the original unmodified:

test3 (df)
df

moral of the story

The following code is pointless:

void rcpp_stuff(const Rcpp::<object> obj) { ... }

because const will just be ignored anyway. Never forget that! Easy way is never to pretend that const Rcpp::<object> has any meaning, and just write this instead:

void rcpp_stuff(Rcpp::<object> obj) { ... }