在包 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_afoo_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++ 代码的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

scala中基于包的自动修复目录结构

继承场景中的 C++ 交换问题

如何重新导出 CLR c++ 静态库

我们又来了:将一个元素附加到 R 中的列表中

我们又来了:将一个元素附加到 R 中的列表中

在路径中制作所有目录的优雅方式