Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling C code from an R package, within C

Tags:

c

r

Is it possible to call C (or C++) functions from an existing R package, within additional C code?

For example, the function marginTable() in my package rje uses a C function of the same name. I want to create a new package which contains more C code, some of which could make use of the C version of marginTable(). Can I call that function from within the new C code, other than just by copying the C code to the new file and package?

Or is it just bad practice to use internal code like this?

[Various people have asked about calling the compiled code from another R package, but all want to do it within R, not with C code.]

like image 580
rje42 Avatar asked Sep 09 '14 08:09

rje42


2 Answers

The R_RegisterCCallable / R_GetCCallable solution pointed to by @BrodieG is probably better than the one below, at least when one can modify the package where registration is required and where the choice of function to call is straight-forward (the example below came from more-or-less complicated R code that chooses one of several functions to pass to C, much like lapply's FUN argument, where choice of function is much easier to implement in R than C). Also relevant is Linking to other packages when wanting to expose / access many functions.

A related possibility is to register your C functions in the rje package, using something like, in R_init_rje.c

#include <Rinternals.h>
#include <R_ext/Rdynload.h>

SEXP rje(SEXP who) {
    Rprintf("Hello %s\n", CHAR(STRING_ELT(who, 0)));
    return R_NilValue;
}

static const R_CallMethodDef callMethods[] = {
    {".rje", (DL_FUNC) &rje, 1},
    {NULL, NULL, 0}
};

void R_init_rje(DllInfo * info)
{
    R_registerRoutines(info, NULL, callMethods, NULL, NULL);
}

and in the NAMESPACE

useDynLib(rje, .registration=TRUE)

The address of the C-level entry point is then available in R as

rje_c = getNativeSymbolInfo(".rje", PACKAGE="rje")

and can be used in your other package by using this as an argument to a C function, e.g.,

.Call(.use_rje, rje_c$address, "A User")

with

#include <Rinternals.h>
#include <R_ext/Rdynload.h>

/* convenience definition of the function template */
typedef SEXP RJE_C_FUN(SEXP who);

SEXP use_rje(SEXP rje_c_fun, SEXP who) {
    /* retrieve the function pointer, using an appropriate cast */
    RJE_C_FUN *fun = (RJE_C_FUN *) R_ExternalPtrAddr(rje_c_fun);
    return fun(who);
}

It's too clumsy to illustrate this in a package, but the principle is illustrated by the following file rje.c

#include <Rinternals.h>
#include <R_ext/Rdynload.h>

/* convenience definition of the function template */
typedef SEXP RJE_C_FUN(SEXP who);

SEXP rje(SEXP who) {
    Rprintf("Hello '%s'\n", CHAR(STRING_ELT(who, 0)));
    return R_NilValue;
}

SEXP use_rje(SEXP rje_c_fun, SEXP who) {
    /* retrieve the function pointer, using an appropriate cast */
    RJE_C_FUN *fun = (RJE_C_FUN *) R_ExternalPtrAddr(rje_c_fun);
    return fun(who);
}

static const R_CallMethodDef callMethods[] = {
    {".rje", (DL_FUNC) &rje, 1},
    {".use_rje", (DL_FUNC) &use_rje, 2},
    {NULL, NULL, 0}
};

void R_init_rje(DllInfo * info)
{
    R_registerRoutines(info, NULL, callMethods, NULL, NULL);
}

Compile with R CMD SHLIB rje.c, and use as

> dyn.load("rje.so")
> .Call(".use_rje", getNativeSymbolInfo("rje")$address, "A User")
Hello 'A User'
NULL
like image 160
Martin Morgan Avatar answered Oct 11 '22 22:10

Martin Morgan


Yes it is possible, and yes there are simple examples.

See for example our (recent-ish) RApiSerialize package which provides serialize() for use by other CRAN packages such as our RcppRedis package.

Other packages do it as well:

  • xts uses C code from zoo;
  • lme4 uses code from Matrix; and
  • expm which I use in RcppKalman.

In all examples does the exporter declares what is being made available, and the importer declares it as used.

In that setup, R can then do the rest -- without explicit linking.

like image 35
Dirk Eddelbuettel Avatar answered Oct 11 '22 21:10

Dirk Eddelbuettel