在 R 中使用 C++ 库的最佳方式是什么,希望保留 C++ 数据结构。我根本不是 C++ 用户,因此我不清楚可用方法的相对优点。R-ext 手册似乎建议用 C 包装每个 C++ 函数。然而,至少存在四到五种其他合并 C++ 的方法。

有两种方法是具有相似血统的软件包,即 Rcpp(由多产的溢出者 Dirk Eddelbuettel 维护)和 RcppTemplate 软件包(均在 CRAN 上),两者之间有什么区别?

R forge 上有另一个包 rcppbind,声称采用不同的方法来绑定 C++ 和 R(我不知道如何告诉)。

CRAN 上提供的内联包声称允许内联 C/C++,除了允许代码内联 w/R 之外,我不确定这与内置功能有何不同。

最后,RSwig 似乎是 在野外 但目前尚不清楚它的支持程度如何,因为 作者页面 已经很多年没有更新了。

我的问题是,这些不同方法的相对优点是什么。哪些是最便携和最强大的,哪些是最容易实现的。如果您计划在 CRAN 上分发软件包,您会使用哪种方法?

有帮助吗?

解决方案

首先,免责声明:我用 RCPP 每时每刻。事实上,当(从 Rcpp 更名时)RcppTemplate 已经被孤立并且两年没有更新时,我开始以其最初的名称 Rcpp 来维护它(在该名称下它已被贡献给 RQuantLib)。那是大约一年前的事,我做了一些增量更改,您可以在更改日志中找到这些更改。

现在,RcppTemplate 在整整三十五个月没有任何更新或修复后最近又回来了。它包含有趣的新代码,但它似乎不向后兼容,因此我不会在已经使用过 Rcpp 的地方使用它。

RCPP绑定 每当我检查时都没有非常积极地维护。Whit Armstrong 还有一个模板化界面包,称为 抽象.

排队 是完全不同的东西:它通过将程序“嵌入”为 R 字符串来简化编译/链接周期,然后进行编译、链接和加载。我已经与 Oleg 讨论了内联支持 Rcpp 的问题,这会很好。

痛饮 也很有趣。Joe Wang 在那里做了出色的工作,并包装了 R 的所有 QuantLib。但当我上次尝试时,由于 R 内部的一些变化,它不再起作用。据 Swig 团队的某人透露,Joe 可能仍在研究它。无论如何,Swig 的目标是更大的库。该项目可能会复兴,但并非没有技术挑战。

另一个提及应该去 R内部 它与 Rcpp 配合使用,让您可以将 R 嵌入到 C++ 应用程序中。

总结一下: RCPP 对我来说效果很好,特别是对于您只想添加一两个功能的小型探索性项目。它的重点是易用性,并且允许您“隐藏”一些使用起来并不总是有趣的 R 内部结构。我认识一些其他用户,我曾通过电子邮件时断时续地为他们提供过帮助。所以我想说,就选这个吧。

我的“R HPC 简介”教程提供了一些 Rcpp、RInside 和内联示例。

编辑: 让我们看一个具体的例子(取自“HPC with R Intro”幻灯片,借用自 Stephen Milborrow,而 Stephen Milborrow 则来自 Venables 和 Ripley)。任务是枚举每个位置仅包含单个数字的 2x2 矩阵行列式的所有可能组合。这可以通过巧妙的矢量化方式(正如我们在教程幻灯片中讨论的那样)或通过暴力来完成,如下所示:

#include <Rcpp.h>

RcppExport SEXP dd_rcpp(SEXP v) {
  SEXP  rl = R_NilValue;        // Use this when there is nothing to be returned.
  char* exceptionMesg = NULL;   // msg var in case of error

  try {
    RcppVector<int> vec(v);     // vec parameter viewed as vector of ints
    int n = vec.size(), i = 0;
    if (n != 10000) 
       throw std::length_error("Wrong vector size");
    for (int a = 0; a < 9; a++)
      for (int b = 0; b < 9; b++)
        for (int c = 0; c < 9; c++)
          for (int d = 0; d < 9; d++)
            vec(i++) = a*b - c*d;

    RcppResultSet rs;           // Build result set to be returned as list to R
    rs.add("vec", vec);         // vec as named element with name 'vec'
    rl = rs.getReturnList();    // Get the list to be returned to R.
  } catch(std::exception& ex) {
    exceptionMesg = copyMessageToR(ex.what());
  } catch(...) {
    exceptionMesg = copyMessageToR("unknown reason");
  }

  if (exceptionMesg != NULL) 
     Rf_error(exceptionMesg);

  return rl;
}

如果您将其另存为,例如, dd.rcpp.cpp 并有 RCPP 安装后,只需使用

PKG_CPPFLAGS=`Rscript -e 'Rcpp:::CxxFlags()'`  \
    PKG_LIBS=`Rscript -e 'Rcpp:::LdFlags()'`  \
    R CMD SHLIB dd.rcpp.cpp

建立一个共享库。我们用 Rscript (或者 r)询问 RCPP 关于其标头和库位置。构建完成后,我们可以从 R 加载并使用它,如下所示:

dyn.load("dd.rcpp.so")

dd.rcpp <- function() {
    x <- integer(10000)
    res <- .Call("dd_rcpp", x)
    tabulate(res$vec)
}

以同样的方式,您可以发送向量、矩阵……轻松后端各种 R 和 C++ 数据类型。希望这有所帮助。

编辑2(大约五年多后):

所以这个答案刚刚得到了赞成票,因此在我的队列中出现。A 很多 自从我写它以来已经过去了很长时间,Rcpp 已经得到了 很多 功能更加丰富。所以我很快就写了这个

#include <Rcpp.h>

// [[Rcpp::export]]
Rcpp::IntegerVector dd2(Rcpp::IntegerVector vec) {
    int n = vec.size(), i = 0;
    if (n != 10000) 
        throw std::length_error("Wrong vector size");
    for (int a = 0; a < 9; a++)
        for (int b = 0; b < 9; b++)
            for (int c = 0; c < 9; c++)
                for (int d = 0; d < 9; d++)
                    vec(i++) = a*b - c*d;
    return vec;
}

/*** R
x <- integer(10000)
tabulate( dd2(x) )
*/

可以将其与文件中的代码一起使用,如下所示 /tmp/dd.cpp

R> Rcpp::sourceCpp("/tmp/dd.cpp")    # on from any other file and path

R> x <- integer(10000)

R> tabulate( dd2(x) )
 [1]  87 132 105 155  93 158  91 161  72 104  45 147  41  96
[15]  72 120  36  90  32  87  67  42  26 120  41  36  27  75
[29]  20  62  16  69  19  28  49  45  12  18  11  57  14  48
[43]  10  18   7  12   6  46  23  10   4  10   4   6   3  38
[57]   2   4   2   3   2   2   1  17
R> 

一些主要差异是:

  • 更简单的构建:只是 sourceCpp() 它;甚至最后执行 R 测试代码
  • 成熟的 IntegerVector 类型
  • 异常处理包装器自动添加 sourceCpp() 代码生成器
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top