假设我在R中有两个包,第一个命名foo,第二个命名bar.我希望在一个独立于平台且与CRAN策略一致的方式中包含C函数foo并共享该功能bar.
这样做的首选方法是什么,我应该如何使用函数注册和动态库?
我的问题的目的是,即使我阅读了我能找到的所有文件,但我没有发生任何明显的事情,我不确定最可持续的行动方案是什么.
例:
假设在一个包中foo,我定义了一个C函数addinc,它增加了两个数字.
#include <R.h>
#include <Rinternals.h>
SEXP addinc(SEXP x_, SEXP y_) {
double x = asReal(x_);
double y = asReal(y_);
double sum = x + y;
return ScalarReal(sum);
}
Run Code Online (Sandbox Code Playgroud)
在同一个包中,我可以尝试调用通过该接口addinc命名的R函数.addinr.Call
addinr <- function(x,y){
.Call("addinc", x, y, PACKAGE="foo")
}
Run Code Online (Sandbox Code Playgroud)
但是,在构建,检查和安装软件包时,运行会addinr返回下面的错误,可能是因为该函数尚未在R中注册.
library(foo)
addinr(1,2)
Run Code Online (Sandbox Code Playgroud)
.Call中的错误("addinc",x,y,PACKAGE ="foo"):
"addinc"对于包"foo"的.Call()不可用
在我看来,解决这个问题的最简单方法是通过添加useDynLib(foo)到fooNAMESPACE文件为编译代码构建动态库.这似乎解决了这个问题,因为我现在可以addinr()毫无问题地打电话.而且,我可以.Call("addinc", ..., PACKAGE="foo")直接在R内运行.
然而,我的真正问题是,当第二个包bar应该使用foos时addinc.例如,假设bar定义一个函数multiplyinr如下.
multiplyinr <- function(x,y){
ans <- 0
for(i in 1:y) ans <- .Call("addinc", ans, x, PACKAGE="foo")
ans
}
Run Code Online (Sandbox Code Playgroud)
事实上,这完全正常,我可以multiplyinr在R中调用.但是,在构建和检查时bar,我收到一个注释,抱怨bar从不同的包调用外语函数的事实.
外部函数调用另一个包:
.Call("addinc",...,PACKAGE ="foo")
请参阅"Writing R Extensions"手册中的"系统和外语接口"一章.
根据这个问题,该软件包bar不适合提交给CRAN,因为使用.Call()这种方式不像" 写入R扩展"手册中所解释的那样被认为是"可移植的" .
总之,在其NAMESPACE文件中foo包含a 的简单解决方案useDynLib(foo)似乎并没有削减它.因此我的问题是:与其他软件包共享C函数的首选方法是什么?
此外:
使用useDynLib()真正危险还是与CRAN政策不一致?useDynLib()在NAMESPACE文件中声明作为手动注册和构建共享库的替代方法的目的是什么?
手动注册C函数和扩展共享库会改变任何东西(即使用R_RegisterCCallable()或R_registerRoutines())吗?
一般的想法是,使用eg创建的"本机符号信息"对象useDynLib(<pkg>, <symbol>)不是包的公共API的一部分,因此客户端包不应该直接调用它们(假设它们可以在包的未来版本中更改) ).
有两种方法可以"导出"已编译的例程以供客户端软件包使用:
foo直接调用本机例程,或者R_RegisterCCallable()/ R_GetCCallable()pair函数获取指向所需函数的指针.(包foo将调用R_RegisterCCallable()以使某些功能可用;客户端包bar将调用R_GetCCallable()以获取指向该函数的指针)换句话说,如果包作者'注册'他们的C函数,他们声明它们是其包的公共C API的一部分,并允许客户端包通过该接口使用/调用它.