Search code examples
cr

Calling C code from an R package, within C


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.]


Solution

  • 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