Iñaki Ucar — written Aug 4, 2017 — source
Sitting on top of R’s external pointers, the
RcppXPtr class provides
a powerful and generic framework for
Passing user-supplied C++ functions
to a C++ backend. This technique is exploited in the
RcppDE package, an
efficient C++ based implementation of the
DEoptim package that
accepts optimisation objectives as both R and compiled functions (see
demo("compiled", "RcppDE") for further details). This solution has a
couple of issues though:
XPtrto R space.
XPtrhandling with RcppXPtrUtils
In a nutshell, RcppXPtrUtils provides functions for dealing with these
two issues: namely,
checkXPtr. As a package author,
you only need to 1) import and re-export
cppXPtr to compile code and
transparently retrieve an
XPtr, and 2) use
checkXPtr to internally
check function signatures.
cppXPtr works in the same way as
Rcpp::cppFunction, but instead of
returning a wrapper to directly call the compiled function from R, it
XPtr to be passed to, unwrapped and called from C++. The
returned object is an R’s
externalptr wrapped into a class called
XPtr along with additional information about the function signature.
'double foo(int a, double b)' <pointer: 0x55e176e4c3f0>
checkXptr function checks the object against a given
signature. If the verification fails, it throws an informative error:
Error in checkXPtr(ptr, "int", c("int", "double")): Bad XPtr signature: Wrong return type 'int', should be 'double'.
Error in checkXPtr(ptr, "int", c("int")): Bad XPtr signature: Wrong return type 'int', should be 'double'. Wrong number of arguments, should be 2'.
Error in checkXPtr(ptr, "int", c("double", "std::string")): Bad XPtr signature: Wrong return type 'int', should be 'double'. Wrong argument type 'double', should be 'int'. Wrong argument type 'std::string', should be 'double'.
First, let us define a templated C++ backend that performs some processing with a user-supplied function and a couple of adapters:
Note that the user-supplied function takes two arguments: one is also user-provided and the other is provided by the backend itself. This core is exposed through the following R function:
Finally, we can compare the
XPtr approach with a pure R-based one,
and with a compiled function wrapped in R, as returned by
Unit: microseconds expr min lq mean median execute(func_r, 1.5) 14364.442 14539.147 15269.3730 14679.8990 execute(func_r_cpp, 1.5) 13738.891 13981.898 14783.3359 14290.0630 execute(func_cpp, 1.5) 273.006 289.839 377.2792 356.6705 uq max neval 16158.6100 19749.851 100 15710.1205 18902.110 100 421.6235 2095.696 100