在包 BAR 中使用 R 包 FOO 中的 C++ 代码的最佳方法
Posted
技术标签:
【中文标题】在包 BAR 中使用 R 包 FOO 中的 C++ 代码的最佳方法【英文标题】:Best way to use c++ code from R package FOO in package BAR 【发布时间】:2014-01-07 21:27:00 【问题描述】:我正在尝试使用 Rcpp 定义一个函数来加速。情况如下:
我有一个包 FOO,其中包含很多 C++ 代码(我自己的包,目前不使用 Rcpp),其中定义了一组函数,例如foo_a 和 foo_b。 在另一个包 BAR(使用 Rcpp)中,我正在定义一个函数(使用 Rcpp 属性),我想在其中调用函数 foo_a 和 foo_b。我该如何解决这个问题?在其他帖子中查看了一下,我发现我以某种方式在 FOO 中包含头文件并在 BAR 中使用属性// [[Rcpp::depends(FOO)]]
,但我似乎错过了一些要点。关于如何做的任何提示?
最好的拉尔斯
编辑:感谢我喜欢 Kevin Usheys 方法并尝试实施它的 cmets。但是,经过一些编码后,我意识到我实际上不需要 FOO 的函数,而是一个类及其公共函数。我想我不能做你建议上课的技巧。我最终将来自 FOO 的类的源文件放在 BAR src 目录中(这不是最好的方法,因为我现在有两个版本的相同代码)。然而,目前这个黑客对我有用。
【问题讨论】:
将它们放入包 FOO 的 inst/include 中(并在 src/Makevars 中设置 -I../inst/include;让包 BAR 使用 RcppDepends(FOO) -- 仅一份,两个包使用看看我写的关于 RcppXts 的文章。 谢谢德克。我将看看 RcppXts。我不认为上课也是可能的。 【参考方案1】:我会推荐以下选项之一:
如果foo_a
和foo_b
足够简单,只需将它们作为inline
函数放在FOO
的标头中,并将这些标头放在FOO/inst/include/FOO.h
中。然后,当您在 BAR
上调用 compileAttributes
(或者可能是 load_all
)时,Rcpp::depends(FOO)
将包含此文件。
否则,请考虑使用 R 的注册模型注册函数。这是一个多一点的工作,但这是可以忍受的。编写 R 扩展有详细信息。我建议将所有注册逻辑放在FOO
中,以便客户端包BAR
只需使用它。例如,我会在FOO
的标题中有这样的foo_a
,例如在FOO/inst/include/FOO.h
:
#ifdef COMPILING_FOO
inline int foo_a(int x, double y, const std::string& z)
typedef int (*Fun)(int, double, const std::string&) ;
static Fun fun = (Fun)R_GetCCallable( "FOO", "foo_a" ) ;
return fun(x,y,z) ;
#else
// just declare it
int foo_a(int x, double y, const std::string& z) ;
#endif
以及在FOO/src
中的一些.cpp
文件中foo_a
的实际定义:
#define COMPILING_FOO
#include <FOO.h>
int foo_a(int x, double y, const std::string& z)
// do stuff
然后,你需要在函数R_init_FOO
中使用R_RegisterCCallable
注册foo_a
:
extern "C" void R_init_FOO( DllInfo* info )
R_RegisterCCallable( "FOO", "foo_a", (DL_FUNC)foo_a );
【讨论】:
当然是R_init_FOO。我想你能猜出我是从哪里得到这个例子的。【参考方案2】:另一个选项,如果您不介意将Rcpp
引入包FOO
- 跟随Section 3.5 of Rcpp-attributes 并执行以下操作:
将 // [[Rcpp::interfaces(cpp)]]
放在 .cpp
源文件的顶部,其中包含您希望其他包可用的功能,
将// [[Rcpp::export]]
放在您要导出的函数前面,
在FOO
的包目录下调用compileAttributes()
生成inst/include
中的文件,然后可以被包BAR
使用,使用// [[Rcpp::depends(FOO)]]
,
安装包FOO
。
如果你正确设置了这个,你应该可以使用这样的模板调用函数(假设foo_a
是从FOO
导出的函数):
// [[Rcpp::depends(FOO)]]
#include <Rcpp.h>
#include <FOO.h>
using namespace Rcpp;
// [[Rcpp::export]]
SEXP some_function()
return FOO::foo_a();
【讨论】:
这实际上与其他两个答案相同,但更容易为您完成:)【参考方案3】:RcppXts 包为著名的xts 包中的一堆函数执行此操作。
编辑:这是来自 xts 的一些代码。
首先,xts::src/init.c 通过十几个类似的声明进行注册
R_RegisterCCallable("xts","do_is_ordered",(DL_FUNC) &do_is_ordered);
第二,xts::inst/include/xtsApi.h 为客户端包提供了一个头文件,例如
SEXP attribute_hidden xtsIsOrdered(SEXP x, SEXP increasing, SEXP strictly)
static SEXP(*fun)(SEXP,SEXP,SEXP) = NULL;
fun = (SEXP(*)(SEXP,SEXP,SEXP)) R_GetCCallable("xts","do_is_ordered");
return fun(x, increasing, strictly);
第三,在 RcppXts 等客户端包中,我们将其(使用 Rcpp 模块)定义为
function("xtsIsOrdered",
&xtsIsOrdered,
List::create(Named("x"),
Named("increasing") = true,
Named("strictly") = true),
"Tests whether object is (strictly) (increasing) ordered");
它将它暴露给 R。我们同样可以从 C++ 代码中调用 C 函数 xtsIsOrdered
。
我删除了之前错误的注释,即函数必须符合“SEXP in, SEXP out”。
【讨论】:
"因为一切都'通过 R' 发生,所以函数必须符合 'SEXP foo(SEXP a, SEXP b, ...) 方案" 不,他们没有。 更好。在某个地方是否有使用此方案交换 C++ 对象的工作示例? lme4 和 Matrix 的基本示例没有 IIRC。 Rcpp 就是一个明显的例子。 好点:Rcpp_init.cpp 执行 R_RegisterCCallable,register.h 执行 R_GetCCallable 的另一端。以上是关于在包 BAR 中使用 R 包 FOO 中的 C++ 代码的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章