是否可以在其他C代码中从现有的R包中调用C(或C ++)函数?
例如,我的包marginTable()
中的函数rje
使用相同名称的C函数。我想创建一个包含更多C代码的新包,其中一些可以使用marginTable()
的C版本。我可以在新的C代码中调用该函数,而不仅仅是将C代码复制到新文件和包中吗?
或者使用这样的内部代码是不好的做法?
[各种各样的人都问过从另一个R包中调用已编译的代码,但是所有人都希望在R 中进行,而不是使用C代码。]
答案 0 :(得分:6)
@BrodieG指向的R_RegisterCCallable
/ R_GetCCallable
解决方案可能比下面的更好,至少当一个人可以修改需要注册的包时,以及要调用的函数的选择直截了当(下面的例子来自或多或少复杂的R代码,它选择了几个函数中的一个传递给C,就像lapply的FUN参数一样,其中函数的选择在R中比在R中更容易实现C)。当想要公开/访问许多功能时,Linking to other packages也是相关的。
相关的可能性是使用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);
}
和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可以完成剩下的工作 - 无需显式链接。