在C中从R包调用C代码

时间:2014-09-09 08:46:36

标签: c r

是否可以在其他C代码中从现有的R包中调用C(或C ++)函数?

例如,我的包marginTable()中的函数rje使用相同名称的C函数。我想创建一个包含更多C代码的新包,其中一些可以使用marginTable()的C版本。我可以在新的C代码中调用该函数,而不仅仅是将C代码复制到新文件和包中吗?

或者使用这样的内部代码是不好的做法?

[各种各样的人都问过从另一个R包中调用已编译的代码,但是所有人都希望在R 中进行,而不是使用C代码。]

2 个答案:

答案 0 :(得分:6)

@BrodieG指向的R_RegisterCCallable / R_GetCCallable解决方案可能比下面的更好,至少当一个人可以修改需要注册的包时,以及要调用的函数的选择直截了当(下面的例子来自或多或少复杂的R代码,它选择了几个函数中的一个传递给C,就像lapply的FUN参数一样,其中函数的选择在R中比在R中更容易实现C)。当想要公开/访问许多功能时,Linking to other packages也是相关的。

相关的可能性是使用R_init_rje.c

中的内容在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);
}

和NAMESPACE

useDynLib(rje, .registration=TRUE)

然后可以在R中将C级入口点的地址作为

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

并且可以在其他包中使用它作为C函数的参数,例如,

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

#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);
}

在一个软件包中说明这一点太笨拙,但原则由以下文件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);
}

使用R CMD SHLIB rje.c进行编译,并使用

> dyn.load("rje.so")
> .Call(".use_rje", getNativeSymbolInfo("rje")$address, "A User")
Hello 'A User'
NULL

答案 1 :(得分:5)

是的,这是可能的,是的,有简单的例子。

例如参见我们的(最近的) RApiSerialize包 它提供serialize()供其他CRAN包使用,例如我们的 RcppRedis包。

其他包也可以这样做:

在所有示例中,导出器声明了可用的内容,导入器将其声明为已使用。

在该设置中,R可以完成剩下的工作 - 无需显式链接。