I'm trying to speed up a loop over the dimensions of an R-array using Rcpp. The array class is from the rcpp-gallery: (https://github.com/RcppCore/rcpp-gallery/blob/gh-pages/src/2014-03-21-simple-array-class.Rmd
// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
#include <Rcpp.h>
using namespace Rcpp ;
/*
******************************************************************************
Offset and Array classes based on code by Romain Francois copied from
http://comments.gmane.org/gmane.comp.lang.r.rcpp/5932 on 2014-01-07.
******************************************************************************
*/
class Offset{
private:
IntegerVector dim ;
public:
Offset( IntegerVector dim ) : dim(dim) {}
int operator()( IntegerVector ind ){
int ret = ind[0] ;
int offset = 1 ;
for(int d=1; d < dim.size(); d++) {
offset = offset * dim[d-1] ;
ret = ret + ind[d] * offset ;
}
return ret ;
} ;
IntegerVector getDims() const {
return(dim) ;
};
} ;
class Array : public NumericVector {
private:
// NumericVector value;
Offset dims ;
public:
//Rcpp:as
Array( SEXP x) : NumericVector(x),
dims( (IntegerVector)((RObject)x).attr("dim") ) {}
Array( NumericVector x, Offset d ): NumericVector(x),
dims(d) {}
Array( Dimension d ): NumericVector( d ), dims( d ) {}
IntegerVector getDims() const {
return(dims.getDims());
};
NumericVector getValue() const {
return(*((NumericVector*)(this)));
};
inline double& operator()( IntegerVector ind) {
int vecind = dims(ind);
NumericVector value = this->getValue();
return value(vecind);
} ;
// change dims without changing order of elements (!= aperm)
void resize(IntegerVector newdim) {
int n = std::accumulate((this->getDims()).begin(), (this->getDims()).end(), 1,
std::multiplies<int>());
int nnew = std::accumulate(newdim.begin(), newdim.end(), 1,
std::multiplies<int>());
if(n != nnew) stop("old and new old dimensions don't match.");
this->dims = Offset(newdim);
} ;
} ;
namespace Rcpp {
// wrap(): converter from Array to an R array
template <> SEXP wrap(const Array& A) {
IntegerVector dims = A.getDims();
//Dimension dims = A.getDims();
Vector<REALSXP> x = A;
x.attr( "dim" ) = wrap(dims);
return x;
}
}
// [[Rcpp::export]]
int runloop(Array& myarray) {
IntegerVector myarrayDims = myarray.getDims();
for (int j = 0; j < myarrayDims[1]; j++) {
for (int k = 0; k < myarrayDims[2]; k++) {
for (int l = 0; l < myarrayDims[3]; l++) {
for (int i = 0; i < myarrayDims[0]; i++) {
myarray({i, j, k, l}) = i + j + k +l;
}
}
}
}
return 0;
}
Sourcing and executing this code from an interactive R session works as intended.
library(Rcpp)
sourceCpp(file.path(".", "minimalExample.cpp"))
inputArray <- array(data = rnorm(10^4), dim = c(10, 10, 10, 10))
runloop(inputArray)
inputArray
But when I move the .cpp file into the src-folder of my R package structure, the build fails, because the class Array is not recognized (see screenshot of build errors)
The content of the RcppExports.cpp after the failed build is as follows:
// Generated by using Rcpp::compileAttributes() -> do not edit by hand
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
#include <RcppArmadillo.h>
#include <Rcpp.h>
using namespace Rcpp;
// runloop
int runloop(Array& myarray);
RcppExport SEXP _foobar_runloop(SEXP myarraySEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Array& >::type myarray(myarraySEXP);
rcpp_result_gen = Rcpp::wrap(runloop(myarray));
return rcpp_result_gen;
END_RCPP
}
static const R_CallMethodDef CallEntries[] = {
{"_foobar_runloop", (DL_FUNC) &_foobar_runloop, 1},
{NULL, NULL, 0}
};
RcppExport void R_init_foobar(DllInfo *dll) {
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
R_useDynamicSymbols(dll, FALSE);
}
How can i get my simple loop running? Thanks :)
ps: my sessionInfo() in R-Studio returns:
R version 3.6.1 (2019-07-05)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19041)
Matrix products: default
locale:
[1] LC_COLLATE=English_Germany.1252 LC_CTYPE=English_Germany.1252 LC_MONETARY=English_Germany.1252 LC_NUMERIC=C
[5] LC_TIME=English_Germany.1252
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] Rcpp_1.0.5
loaded via a namespace (and not attached):
[1] compiler_3.6.1 tools_3.6.1
There is a saying that you need to walk before you can run. You missed the jogging step between the walk and the sprint ;-).
Having code that compiles in a single function that you source is not the same as having it in a full-blown package. What you desire to do can be done and has been done. In short, while you have the code wrote in the source file, it also needs to be present in the generated file src/RcppExports.cpp
.
One trick you are missing now is described in the Rcpp Attributes vignette in the section Types in Generated Code, and I quote (editing out markdown/latex)
Types in Generated Code
In some cases the signatures of the C++ functions that are generated within
RcppExports.cpp
may have additional type requirements beyond the core standard library andRcpp
types (e.g.CharacterVector
,NumericVector
, etc.). Examples might include convenience typedefs, as/wrap handlers for marshaling between custom types and SEXP, or types wrapped by the RcppXPtr
template.In this case, you can create a header file that contains these type definitions (either defined inline or by including other headers) and have this header file automatically included in
RcppExports.cpp
. Headers named with the conventionpkgname_types
are automatically included along with the generated C++ code. For example, if your package is namedfastcode
then any of the following header files would be automatically included inRcppExports.cpp
:src/fastcode_types.h src/fastcode_types.hpp inst/include/fastcode_types.h inst/include/fastcode_types.hpp
[...]