在R包中使用C++库

Pet*_*ter 16 c++ r rcpp

在R中使用C++库的最佳方法是什么,希望保留C++数据结构.我不是一个C++用户,所以我不清楚可用方法的相对优点.R-ext手册似乎建议用C语言包装每个C++函数.但是,至少有四到五种其他的C++结合方法.

两种方式是具有类似谱系的包,Rcpp(由多产的overflower Dirk Eddelbuettel维护)和RcppTemplate包(都在CRAN上),两者之间有什么区别?

另一个可用的rcppbind包,声称采用不同的方法绑定C++和R(我不知道如何知道).

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

而且,最后RSwig似乎是在野外,但目前尚不清楚它是如何支持的,因为作者的页面多年来一直没有更新.

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

Dir*_*tel 18

首先,免责声明:我一直使用Rcpp.实际上,当RcppTemplate已经成为孤儿并且两年没有更新时,RcppTemplate已经被孤立了,但是我开始将它保持在其初始名称Rcpp(在此之后它已经贡献给RQuantLib).大约一年前,我已经做了一些增量更改,你可以在ChangeLog中找到它们.

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

每当我检查时,Rcppbind都没有得到很好的维护.Whit Armstrong还有一个名为rabstraction的模板化界面包.

内联是完全不同的东西:它通过将程序"嵌入"为R字符串然后被编译,链接和加载来简化编译/链接循环.我已经和Oleg讨论了内联支持Rcpp的问题.

Swig也很有趣.Joe Wang在那里做了很棒的工作并将所有的QuantLib包裹在R中.但是当我上次尝试它时,由于R内部的一些变化,它不再起作用.根据Swig团队的一位人士的说法,Joe可能还在努力.无论如何,Swig的目标是更大的图书馆.这个项目可能会复兴,但并非没有技术挑战.

另一个应该提到的是与Rcpp配合使用的RInside,它允许你将R嵌入到C++应用程序中.

总而言之:Rcpp 对我来说效果很好,特别是对于你只想添加一两个函数的小型探索项目.它的重点是易用性,它允许你"隐藏"一些R内部结构并不总是很有趣.我知道其他一些用户通过电子邮件帮助过我.所以我会说这个.

我的'带有R的HPC简介'教程有一些Rcpp,RInside和inline的例子.

编辑: 那么让我们看一个具体的例子(取自'HP In with R Intro'幻灯片,并从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;
}
Run Code Online (Sandbox Code Playgroud)

如果您将其保存为,dd.rcpp.cpp并且安装了Rcpp,那么只需使用即可

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

构建共享库.我们使用Rscript(或r)向Rcpp询问其标题和库位置.一旦构建,我们可以从R加载和使用它,如下所示:

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

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

以同样的方式,您可以轻松地发送各种R和C++数据类型的向量,matrics,....希望这有点帮助.

编辑2(大约五年+之后):

所以这个答案只是得到了一个upvote,因此在我的队列中冒出来.一个大量的时间已经过去了,因为我写的,和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) )
*/
Run Code Online (Sandbox Code Playgroud)

可以如下使用文件中的代码 /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> 
Run Code Online (Sandbox Code Playgroud)

一些关键的区别是:

  • 更简单的构建:就是sourceCpp()这样; 甚至在最后执行R测试代码
  • 完整的IntegerVector类型
  • sourceCpp()代码生成器自动添加异常处理包装器

  • Dirk,也许现在是时候谈谈让C++与R ...一起工作的快速小说:(! (2认同)