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 e, int n, SEXP rho); SEXP R_forceAndCall
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 — aLANGSXP
linked list whose head is the function object and whose tail is function arguments. You construct such lists with theLCONS
macro, end them withR_NilValue
(which corresponds to R’sNULL
), and assign argument names using theSET_TAG
macro (if needed).SEXP rho
— the environment in which the evaluation will happen.
This suggests the following implementation:
#include <Rinternals.h>
(SEXP f, SEXP x, SEXP rho) {
SEXP R_apply_fun= PROTECT(LCONS(f, LCONS(x, R_NilValue)));
SEXP call = R_forceAndCall(call, 1, rho);
SEXP val (1);
UNPROTECTreturn 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:
<- function(f, x) .Call(R_apply_fun, f, x, environment()) apply_fun
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:
::load_all()
devtoolsapply_fun(function(x) x+1, 3)