Call an R function from C
Published on
This article explains how to call an R function from a C extension.
The “Writing R Extensions” guide gives such an example, but their approach — composing an expression and then evaluating it — strikes me as unnecessarily complicated and roundabout.
Instead, you can use the R_forceAndCall function:
SEXP R_forceAndCall(SEXP e, int n, SEXP rho);This C function is not documented or part of the official
API, so use it on your own risk. We can get some idea about what it
does by reading the documentation
for the corresponding R function, forceAndCall:
Call a function with a specified number of leading arguments forced before the call if the function is a closure.
So the arguments to R_forceAndCall are:
int n— the number of arguments to force.SEXP e— a call object — aLANGSXPlinked list whose head is the function object and whose tail is function arguments. You construct such lists with theLCONSmacro, end them withR_NilValue(which corresponds to R’sNULL), and assign argument names using theSET_TAGmacro (if needed).SEXP rho— the environment in which the evaluation will happen.
This suggests the following implementation:
#include <Rinternals.h>
SEXP R_apply_fun(SEXP f, SEXP x, SEXP rho) {
SEXP call = PROTECT(LCONS(f, LCONS(x, R_NilValue)));
SEXP val = R_forceAndCall(call, 1, rho);
UNPROTECT(1);
return val;
}The code relies on the following comment found in
do_lapply:
/* Notice that it is OK to have one arg to LCONS do memory
allocation and not PROTECT the result (LCONS does memory
protection of its args internally), but not both of them,
since the computation of one may destroy the other */
The calling environment, rho, needs to be passed in from
the R land. Even though R’s own functions can access the current
environment through R_GlobalContext->sysparent (see
do_envir), neither R_GlobalContext nor
do_envir is part of the public API (i.e. they are not
declared in installed headers).
So the R wrapper for R_apply_fun should be something
like this:
apply_fun <- function(f, x) .Call(R_apply_fun, f, x, environment())The full example can be downloaded as a git repo:
git clone https://ro-che.info/files/2017-08-18-call-r-function-from-c.git
To test it, install the devtools package, go to the
cloned repo, launch R, and run:
devtools::load_all()
apply_fun(function(x) x+1, 3)