在R中使用C ++库的最佳方法是什么,希望保留C ++数据结构。我不是一个C ++用户,所以我不清楚可用方法的相对优点。 R-ext手册似乎建议用C语言包装每个C ++函数。但是,至少有四到五种其他的C ++函数存在。
两种方式是具有相似谱系的包装,Rcpp(由多产的overflower Dirk Eddelbuettel维护)和RcppTemplate包装(均在CRAN上),两者之间有什么区别?
另一个可用的rcppbind软件包,它声称采用不同的方法来绑定C ++和R(我不知道如何知道)。
CRAN上可用的包内联,声称允许内联C / C ++我不确定这与内置功能有什么不同,除了允许代码内联w / R.
最后,RSwig似乎是in the wild,但目前还不清楚它是如何支持的,因为author's page多年来一直没有更新。
我的问题是,这些不同方法的相对优点是什么。哪些是最便携和最强大的,哪些是最容易实现的。如果您打算在CRAN上分发一个包,您会使用哪种方法?
答案 0 :(得分:17)
首先,免责声明:我一直使用Rcpp。实际上,当(从Rcpp那时候被重命名)RcppTemplate已经成为孤儿并且两年没有更新时,我开始将其保持在其初始名称Rcpp下(在此基础上它被贡献给RQuantLib )。大约一年前,我已经做了一些增量更改,你可以在ChangeLog中找到它们。
现在RcppTemplate最近在整整三十五个月后回来,没有任何更新或修复。它包含有趣的新代码,但似乎它不向后兼容所以我不会在我已经使用过Rcpp的地方使用它。
每当我检查时,Rcppbind都没有得到很好的维护。 Whit Armstrong还有一个名为rabstraction的模板化界面包。
Inline是完全不同的东西:它通过将程序“嵌入”为R字符串然后被编译,链接和加载来简化编译/链接循环。我已经和Oleg讨论了内联支持Rcpp的问题,这很不错。
Swig也很有趣。 Joe Wang在那里做了很棒的工作并将所有的QuantLib包裹在R中。但是当我上次尝试它时,由于R内部的某些变化,它不再起作用。根据Swig团队的一位人士的说法,Joe可能还在努力。无论如何,Swig的目标是更大的图书馆。这个项目可能会复兴,但并非没有技术挑战。
另一个值得提及的是RInside,它适用于Rcpp,并允许您将R嵌入到C ++应用程序中。
总而言之:Rcpp对我很有用,特别是对于你只想添加一两个函数的小型探索性项目。它的重点是易用性,它允许你“隐藏”一些R内部结构并不总是很有趣。我知道其他一些用户通过电子邮件帮助过我。所以我会说这个。
我的'带有R的HPC简介'教程有一些Rcpp,RInside和内联的例子。
编辑:让我们看一个具体的例子(摘自'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;
}
如果您将其保存为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 ++数据类型的向量,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) )
*/
可以使用文件/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()
代码生成器