在 R 包中使用 C++ 库 [英] Using C++ libraries in an R package

查看:36
本文介绍了在 R 包中使用 C++ 库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

What is the best way to make use of a C++ library in R, hopefully preserving the C++ data structures. I'm not at all a C++ user, so I'm not clear on the relative merits of the available approaches. The R-ext manual seems to suggest wrapping every C++ function in C. However, at least four or five other means of incorporating C++ exist.

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

Two ways are packages w/ similar lineage, the Rcpp (maintained by the prolific overflower Dirk Eddelbuettel) and RcppTemplate packages (both on CRAN), what are the differences between the two?

R forge 上提供的另一个包 rcppbind 声称采用不同的方法来绑定 C++ 和 R(我不知道如何说).

Another package, rcppbind available, on R forge that claims to take a different approach to binding C++ and R (I'm not knowledgeable to tell).

CRAN 上可用的内联包声称允许内联 C/C++ 我不确定这与内置功能不同,除了允许代码与 R 内联.

The package inline available on CRAN, claims to allow inline C/C++ I'm not sure this differs from the built in functionality, aside for allowing the code to be inline w/R.

而且,最后 RSwig 看起来是在野外但它是不清楚它的支持程度,因为 作者页面 多年未更新.

And, finally RSwig which appears to be in the wild but it is unclear how supported it is, as the author's page hasn't been updated for years.

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

My question is, what are the relative merits of these different approaches. Which are the most portable and robust, which are the easiest to implement. If you were planning to distribute a package on CRAN which of the methods would you use?

推荐答案

首先声明:我使用 Rcpp 一直.事实上,当(当时已经从 Rcpp 重命名)RcppTemplate 已经成为孤儿并且两年没有更新时,我开始以它的初始名称 Rcpp 维护它(在该名称下它已被贡献给 RQuantLib).那是大约一年前,我进行了一些增量更改,您可以在 ChangeLog 中找到这些更改.

First off, a disclaimer: I use Rcpp all the time. In fact, when (having been renamed by the time from Rcpp) RcppTemplate had already been orphaned and without updates for two years, I started to maintain it under its initial name of Rcpp (under which it had been contributed to RQuantLib). That was about a year ago, and I have made a couple of incremental changes that you can find documented in the ChangeLog.

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

Now RcppTemplate has very recently come back after a full thirty-five months without any update or fix. It contains interesting new code, but it appears that it is not backwards compatible so I won't use it where I already used Rcpp.

Rcppbind 的维护都不是很积极.Whit Armstrong 还有一个名为 rabstraction 的模板化界面包.

Rcppbind was not very actively maintained whenever I checked. Whit Armstrong also has a templated interface package called rabstraction.

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

Inline is something completely different: it eases the compile / link cycle by 'embedding' your program as an R character string that then gets compiled, linked, and loaded. I have talked to Oleg about having inline support Rcpp which would be nice.

Swig 也很有趣.Joe Wang 在那里做得很好,并为 R 包装了所有的 QuantLib.但是当我上次尝试它时,由于 R 内部的一些变化,它不再起作用.据 Swig 团队的某个人说,Joe 可能还在继续努力.无论如何,Swig 的目标是更大的库.这个项目可能会复兴,但并非没有技术挑战.

Swig is interesting too. Joe Wang did great work there and wrapped all of QuantLib for R. But when I last tried it, it no longer worked due to some changes in R internals. According to someone from the Swig team, Joe may still work on it. The goal of Swig is larger libraries anyway. This project could probably do with a revival but it is not without technical challenges.

另一个值得一提的地方是 RInside,它与 Rcpp 配合使用,可让您将 R 嵌入其中C++ 应用程序.

Another mention should go to RInside which works with Rcpp and lets you embed R inside of C++ applications.

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

So to sum it up: Rcpp works well for me, especially for small exploratory projects where you just want to add a function or two. It's focus is ease of use, and it allows you to 'hide' some of the R internals that are not always fun to work with. I know of a number of other users whom I have helped on and and off via email. So I would say go for this one.

我的使用 R 介绍 HPC"教程有一些 Rcpp、RInside 和内联示例.

My 'Intro to HPC with R' tutorials have some examples of Rcpp, RInside and inline.

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

So let's look at a concrete example (taken from the 'HPC with R Intro' slides and borrowed from Stephen Milborrow who took it from Venables and Ripley). The task is to enumerate all possible combinations of the determinant of a 2x2 matrix containing only single digits in each position. This can be done in clever vectorised ways (as we discuss in the tutorial slides) or by brute force as follows:

#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 已安装,然后只需使用

If you save this as, say, dd.rcpp.cpp and have Rcpp installed, then simply use

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

构建共享库.我们使用Rscript(或r)来询问Rcpp 关于它的头文件和库位置.构建完成后,我们可以从 R 加载和使用它,如下所示:

to build a shared library. We use Rscript (or r) to ask Rcpp about its header and library locations. Once built, we can load and use this from R as follows:

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

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

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

In the same way, you can send vectors, matrics, ... of various R and C++ data types back end forth with ease. Hope this helps somewhat.

编辑 2(大约五年以上):

所以这个答案刚刚得到了赞成票,因此在我的队列中冒泡了.自从我编写它以来已经过去了很多时间,Rcpp 的功能变得很多.所以我很快就写了这个

So this answer just got an upvote and hence bubbled up in my queue. A lot of time has passed since I wrote it, and Rcpp has gotten a lot richer in features. So I very quickly wrote this

#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

which can be used as follows with the code in a file /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()代码生成器自动添加的异常处理包装器
  • simpler build: just sourceCpp() it; even executes R test code at the end
  • full-fledged IntegerVector type
  • exception-handling wrapper automatically added by sourceCpp() code generator

这篇关于在 R 包中使用 C++ 库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆