Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Better idiom for filling a C++ array from a NumericVector

Tags:

c++

r

rcpp

I am using Rcpp to wrap an algorithm written (not by me) in C-like C++ (no STL, no boost, no nothing, as far as I can tell). You can see the implemented algorithm here (I'm wrapping kmeans_w_03). Consequently I'm passing in a numeric vector from R, which then needs to be converted to a double array.

Currently I'm looping element-by-element and filling the one from the 'tother, like this:

SEXP testfn(SEXP weightvec, SEXP cluster_num_k){
    Rcpp::NumericVector weightR(weightvec) ;
    int point_num = weightR.size();
    double weight[point_num] ;
    for(int i = 0; i < point_num; ++i) {
      weight[i] = weightR[i];
    }
}

But with single-element numeric vectors, I can take advantage of Rcpp's beautiful as casting functionality:

int cluster_num = Rcpp::as<int>(cluster_num_k);

Trying something similar for length > 1 numeric vectors, however, leads to a crash or an error, depending on the exact variant of the syntax:

double weight[point_num] = Rcpp::as<double>(weightvec);

I don't necessarily mind the loop, but I'm a total neophyte and suspect there's a better way. I've read through Rcpp-introduction, hadley's wiki tutorial, and RcppExamples and not yet found anything that addresses this question, but that doesn't mean I didn't just miss it. My read of the Doxygen Rcpp docs is that as can cast to a STL vector but not an array (but I have a very hard time reading those docs, so I suspect I'm wrong there). If so, I guess I could cast to a vector and thence to an array....

So my question: is there a better (fewer lines of code, more expressive code, and perhaps even able-to-avoid-a-memory-allocation) way to convert a NumericVector to a double[]?

like image 395
Ari B. Friedman Avatar asked Dec 24 '12 13:12

Ari B. Friedman


2 Answers

If your question is how to COPY data from Rcpp::NumericVector to C++ array, transform() is a good answer. But if you want to get the inner array from Rcpp::NumericVector, which does not include any copying, you can do exactly the same as the extracting array from std::vector:

double* arr = &vec[0];

where vec is a Rcpp::NumericVector.

like image 73
David Avatar answered Oct 27 '22 15:10

David


Ari, in the previous answer, John makes a good point. To extend a little, there are a few issues here

  • you are dealing with a library with antiquated coding standard which wants a double[] or *double
  • you want to use better coding standards and take advantage of Rcpp.

Well, fear not, we have a simple solution. Instantantiate the Rcpp::NumericVector X(weightvec); as usual, and then pass it to your function foo() (or whatever) as

foo(X.begin())

which gives you the required double*, and if needed X.size() provides the length. Because you come from R, you do not need to worry about scope and lifetime as you return to R afterwards.

In case you need to be more explicit, I have also used the uglier &(X[0]).

Also, the Rcpp::as<>() caster also works on std::vector<double>, so you can do

std::vector<double> x = Rcpp::as<std::vector<double> >(weightvec);

but apart from converting to C++ types it gains nothing here.

Finally, in case Josh or other C diehards are nearby, all this uses the fact that the SEXP you from R is guaranteed to have the contiguous C pointer, so you could also go via the very old school REAL(weightvec).

like image 21
Dirk Eddelbuettel Avatar answered Oct 27 '22 16:10

Dirk Eddelbuettel