假设我在 R 中有两个包,第一个名为 foo
,第二个名为 bar
。我想在 foo
中包含一个 C 函数,并以一种独立于平台且符合 CRAN 策略的方式与 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);
}
在同一个包中,我可以尝试通过 .Call
接口(interface)在名为 addinr
的 R 函数中调用 addinc
。
addinr <- function(x,y){
.Call("addinc", x, y, PACKAGE="foo")
}
但是,在构建、检查和安装包时,运行 addinr
会返回以下错误,大概是因为该函数尚未在 R 中注册。
library(foo)
addinr(1,2)
Error in .Call("addinc", x, y, PACKAGE = "foo") :
"addinc" not available for .Call() for package "foo"
在我看来,解决这个问题的最简单方法是通过将 useDynLib(foo)
添加到 foo
的 NAMESPACE 文件来为编译后的代码构建一个动态库.
这似乎可以解决问题,因为我现在可以毫无问题地调用 addinr()
。此外,我可以直接从 R 中运行 .Call("addinc", ..., PACKAGE="foo")
。
然而,我的真正的问题出现在第二个包,比如bar
,应该使用foo
的addinc
。例如,假设 bar
定义了一个函数 multiplyinr
,如下所示。
multiplyinr <- function(x,y){
ans <- 0
for(i in 1:y) ans <- .Call("addinc", ans, x, PACKAGE="foo")
ans
}
事实上,这完全可以正常工作,我可以在 R 中调用 multiplyinr
。但是,在构建和检查 bar
时,我收到一条提示这个事实的注释bar
正在调用来自不同包的外语函数。
Foreign function call to a different package:
.Call("addinc", ..., PACKAGE = "foo")
See chapter ‘System and foreign language interfaces’ in the ‘Writing R Extensions’ manual.
根据 this question ,包 bar
不适合提交给 CRAN,因为以这种方式使用 .Call()
不被视为“可移植”,如 Writing R Extensions manual 中所述。 .
总而言之,让 foo
在其 NAMESPACE 文件中包含一个 useDynLib(foo)
的简单解决方案似乎并不能完全解决问题。因此我的问题是:与其他包共享 C 函数的首选方法是什么?
此外:
使用 useDynLib()
真的很危险或不符合 CRAN 政策吗?在 NAMESPACE 文件中声明 useDynLib()
以替代手动注册和构建共享库的目的是什么?
手动注册 C 函数并构建共享库是否会改变任何东西(即,使用 R_RegisterCCallable()
或 R_registerRoutines()
)?
最佳答案
一般的想法是通过使用例如创建的“本地符号信息”对象。 useDynLib(<pkg>, <symbol>)
不是包的公共(public) API 的一部分,因此客户端包不应该直接调用它们(假设它们可以在包的 future 修订中更改)。
有两种方法可以“导出”已编译的例程以供客户端包使用:
- 只需在
foo
中导出一个 R 包装函数直接调用 native 例程,或者 - 使用
R_RegisterCCallable()
/R_GetCCallable()
一对函数以获得指向所需函数的指针。 (包foo
将调用R_RegisterCCallable()
使某些函数可用;客户端包bar
将调用R_GetCCallable()
以获取指向该函数的指针)
换句话说,如果包作者“注册”了他们的 C 函数,他们就在声明这是他们包的公共(public) C API 的一部分,并允许客户端包通过此接口(interface)使用/调用它。
关于c - 在 R 包中共享已编译的 C 代码并从另一个包运行它的首选方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35103804/