The RcppXPtrUtils package provides the means to compile user-supplied C++ functions with ‘Rcpp’ and retrieve an XPtr that can be passed to other C++ components.
Install the release version from CRAN:
install.packages("RcppXPtrUtils")
The installation from GitHub can be done with the remotes package:
::install_github("Enchufa2/RcppXPtrUtils") remotes
Let’s suppose we have a package with a core written in C++, connected
to an R API with Rcpp
. It accepts a user-supplied R
function to perform some processing:
#include <Rcpp.h>
using namespace Rcpp;
template <typename T>
(T func, double l) {
NumericVector core_processingdouble accum = 0;
for (int i=0; i<1e3; i++)
+= sum(as<NumericVector>(func(3, l)));
accum return NumericVector(1, accum);
}
// [[Rcpp::export]]
(Function func, double l) {
NumericVector execute_rreturn core_processing<Function>(func, l);
}
But calling R from C++ is slow, so we can think about improving the
performance by accepting a compiled function. In order to do this, the
core can be easily extended to accept an XPtr
to a compiled
function:
typedef SEXP (*funcPtr)(int, double);
// [[Rcpp::export]]
(SEXP func_, double l) {
NumericVector execute_cpp= *XPtr<funcPtr>(func_);
funcPtr func return core_processing<funcPtr>(func, l);
}
To easily leverage this feature, the RcppXPtrUtils
package provides cppXPtr()
, which compiles a user-supplied
C++ function using Rcpp::cppFunction()
and returns an
XPtr
:
# compile the code above
# Rcpp::sourceCpp(code='...')
library(RcppXPtrUtils)
<- function(n, l) rexp(n, l)
func_r <- cppXPtr("SEXP foo(int n, double l) { return rexp(n, l); }")
func_cpp
::microbenchmark(
microbenchmarkexecute_r(func_r, 1.5),
execute_cpp(func_cpp, 1.5)
)#> Unit: microseconds
#> expr min lq mean median uq
#> execute_r(func_r, 1.5) 14910.161 16261.928 17628.8078 17468.1140 18635.388
#> execute_cpp(func_cpp, 1.5) 213.123 223.125 310.2708 237.0265 279.808
#> max neval cld
#> 22657.568 100 b
#> 2417.878 100 a
The object returned by cppXPtr()
is just an
externalptr
wrapped into an object of class
XPtr
, which stores the signature of the function. If you
are a package author, you probably want to re-export
cppXPtr()
and ensure that user-supplied C++ functions
comply with the internal signatures in order to avoid runtime errors.
This can be done with the checkXPtr()
function:
func_cpp#> 'SEXP foo(int n, double l)' <pointer: 0x55909eb28830>
checkXPtr(func_cpp, "SEXP", c("int", "double")) # returns silently
checkXPtr(func_cpp, "int", c("int", "double"))
#> Error in checkXPtr(func_cpp, "int", c("int", "double")): Bad XPtr signature:
#> Wrong return type 'int', should be 'SEXP'.
checkXPtr(func_cpp, "SEXP", c("int"))
#> Error in checkXPtr(func_cpp, "SEXP", c("int")): Bad XPtr signature:
#> Wrong number of arguments, should be 2'.
checkXPtr(func_cpp, "SEXP", c("double", "int"))
#> Error in checkXPtr(func_cpp, "SEXP", c("double", "int")): Bad XPtr signature:
#> Wrong argument type 'double', should be 'int'.
#> Wrong argument type 'int', should be 'double'.