Roman Cheplyaka

Call an R function from C

Published on August 18, 2017

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:

  1. int n — the number of arguments to force.
  2. SEXP e — a call object — a LANGSXP linked list whose head is the function object and whose tail is function arguments. You construct such lists with the LCONS macro, end them with R_NilValue (which corresponds to R’s NULL), and assign argument names using the SET_TAG macro (if needed).
  3. 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)