How to call an R function from C

Last year I worked on an analysis project in which we needed to call a statistical function written in R from a C program. There are some great tools to integrate R and C++ (for example Rccp and Rinside), but for this particular project we wanted a plain C solution with minimal dependencies. Here I’ll describe the basics of how to call R from plain C and provide a minimal working example (full code available on github). For more information Writing R Extensions is a wealth of information, but it’s not easy reading for a newcomer to R internals. There are also some great stackoverflow threads on this topic: Calling R Function from C++, and R from C — Simplest Possible Helloworld.

Suppose that we want to create an array in C, pass that array to a function in R, and receive the result of R’s computation. For the sake of illustration we will call a function that simply adds one to each element in the array:

add1 <- function(a) {
  cat("R received: ", a, "\n");

  return(a + 1)
}

To call this function from C we need to perform a few steps:

Set up an embedded R environment

#include <Rinternals.h>
#include <Rembedded.h>

// Intialize the embedded R environment.
int r_argc = 2;
char *r_argv[] = { "R", "--silent" };
Rf_initEmbeddedR(r_argc, r_argv);

Load the R function definition.

We can do this by writing a C version of the R source function:

/**
* Invokes the command source("foo.R").
*/
void source(const char *name)
{
    SEXP e;

    PROTECT(e = lang2(install("source"), mkString(name)));
    R_tryEval(e, R_GlobalEnv, NULL);
    UNPROTECT(1);
}

This snippet introduces several important concepts. R internals use SEXP (S-expressions) to represent function calls and arguments. In this example, the S-expression is basically a pair in which the first element is the function to invoke and the second element is the arguments to the function. The above code builds an SEXP to call the function source with the argument name, and then evaluates that expression in the R environment.

Also notice the use of the PROTECT macro. This macro tells R than an object will be used by C code, so it must not be garbage collected. PROTECT adds the object to a protection stack, so we are done with the object we must call UNPROTECT(1), which pops one object off of the stack. (See Writing R Extensions 5.9.1).

Call the function

Suppose that we want to invoke our R function on the following C array:

int a[] = { 1, 2, 3, 4, 5 };
int alen = 5;

First we need to create an R vector and copy the data from the C array to the vector:

// Load the R function (source function defined above)
source("func.R")

// Allocate an R vector and copy the C array into it.
SEXP arg;
PROTECT(arg = allocVector(INTSXP, alen));
memcpy(INTEGER(arg), a, alen * sizeof(int));

Note the use of the INTEGER macro, which gives us an int* view of the SEXPR. There are corresponding macros for other data types (e.g., REAL).

Next we need to set up and evaluate an SEXP for the add1 function call:

// Setup a call to the R function
SEXP add1_call;
PROTECT(add1_call = lang2(install("add1"), arg));

// Execute the function
int errorOccurred;
SEXP ret = R_tryEval(add1_call, R_GlobalEnv, &errorOccurred);

And finally we can get a C pointer to the function’s result and do something with the results in our C code:

if (!errorOccurred)
{
    double *val = REAL(ret);

    printf("R returned: ");
    for (int i = 0; i < LENGTH(ret); i++)
        printf("%0.1f, ", val[i]);
    printf("\n");
}

Don’t forget to clean up protected memory:

// Unprotect add1_call and arg 
UNPROTECT(2);

// Release R environment
Rf_endEmbeddedR(0);

Compile and run the example

Note that the R_HOME environment variable must be defined when the program is run.

export R_HOME=/Library/Frameworks/R.framework/Resources
export LD_LIBRARY_PATH=$R_HOME/lib

cc -o r_test -g -I$R_HOME/include -L$R_HOME/lib -lR -lRblas r_test.c

./r_test

And the output is:

R received:  1 2 3 4 5
R returned: 2.0, 3.0, 4.0, 5.0, 6.0,

The full example is available on github.

This entry was posted in Programming and tagged . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *