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 — a- LANGSXPlinked list whose head is the function object and whose tail is function arguments. You construct such lists with the- LCONSmacro, end them with- R_NilValue(which corresponds to R’s- NULL), and assign argument names using the- SET_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.gitTo 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)