Search code examples
c++rrcppinline-code

return value from embedded c++ function in R


I've got some R code, that is somewhat slow, so I've been trying to write some c++ code directly in the R code using the 'inline' library.

This works well, and I'm now trying to tweak it.

I've only been able to make it work if I allocate the 'results' data structures in R and pass these as function parameters to the c function. I'm wondering if it is possible to have a non void c/c++ function in the R code, such that memory is allocated and returned from c/c++ and not R.

See example below:

library(inline)
cppSig <- signature(res="numeric",ary="numeric",len="integer")
cppBody <- "
int lens=len[0];
res[0]=0;
  for(int j=0;j<lens;j++)
     res[0] += ary[j];
res[0] /= (double) lens;
#if 0 //Is something like this possible? 
    double *rary = new double[lens];
    for(int i=0;i<lens;i++) rary[i] = ary[i]-res[0];
    return rary;
#endif
"
cfun <- cfunction( sig=list(myMean=cppSig), 
                 body=list(cppBody),verbose=T, 
                 convention=".C", cxxargs="-O3", cppargs="-O3",language="C++")
cfunWrap <- function(x)
  cfun$myMean(res=0,ary=x,length(x))$res


cfunWrap(x=rnorm(100))

Thanks


Solution

  • There are a few things I'd do differently, particularly after even just a casual glance at the Rcpp documentation. So here is just a quick list:

    1. Yes, we can make loops faster. Often by a lot.
    2. Yes, we can return atomic C/C++ types as well as vectors. There are lots of example. One uses wrap() for these non-vector types; vectors of double are returned automatically. But you never ever use new/delete on them. See Writing R Extensions for why.
    3. Yes, you can use the inline package. And we use it a lot. But we never use it with the .C() call convention from the cfunction(). Always use cxxfunction(), or at least enable .Call(). I am not sure how you missed that.
    4. Since Rcpp 0.10.0, we have 'Rcpp Attributes' which are even easier to use than inline and its cxxfunction(). Look around for example for sourceCpp() or cppFunction(), or even read the vignette.
    5. Lastly, you are really missing some elementary stuff. Did you read the pdf vignettes Rcpp-iintroduction and/or Rcpp-FAQ?

    Edit: Okay, here is a full example following the structure of your function (but we can do better, see below for that):

    #include <Rcpp.h>
    
    using namespace Rcpp; 
    
    // [[Rcpp::export]]
    NumericVector monkey(NumericVector ary) {
      int lens = ary.length();   // objects can tell you about their length
      double res=0;
      for(int j=0;j<lens;j++) res += ary[j];
      res /= (double) lens;
    
      NumericVector rary(lens);
      for(int i=0;i<lens;i++) rary[i] = ary[i]-res;
      return rary;
    }
    
    // and we even include some R code to test automagically
    
    /*** R
    set.seed(42)
    x <- rnorm(5)   # just five to keep printout short
    monkey(x)
    cat("Check:")
    x - mean(x)
    */
    

    which, if you call it, also runs the R code at the bottom:

    R> Rcpp::sourceCpp('/tmp/monkey.cpp')
    
    R> set.seed(42)
    
    R> x <- rnorm(5)   # just five to keep printout short
    
    R> monkey(x)
    [1]  0.9296545 -1.0060021 -0.0781755  0.1915587 -0.0370356
    
    R> cat("Check:")
    Check:
    R> x - mean(x)
    [1]  0.9296545 -1.0060021 -0.0781755  0.1915587 -0.0370356
    R>
    

    But one of the key features of Rcpp is that you can even do the vector operation in C++:

    R> cppFunction('NumericVector monkey2(NumericVector x) { return x - mean(x); }')
    R> monkey2(x)
    [1]  0.9296545 -1.0060021 -0.0781755  0.1915587 -0.0370356
    R> 
    

    That just compiled a new one-line C++ function which operated on the whole vector x and ran it.