代码之家  ›  专栏  ›  技术社区  ›  Peter

在R包中使用C++库

  •  15
  • Peter  · 技术社区  · 15 年前

    在R中使用C++库的最好方法是什么,希望保留C++数据结构。我根本不是C++用户,所以我不清楚可用方法的相对优点。R - Ext手册似乎建议包装C中的每一个C++函数,然而,至少有四个或五个其他的结合C++的方法存在。

    有两种方法是具有类似血统的包,RCPP(由多产的溢出器Dirk Eddelbuettel维护)和RCPTemplate包(都在Cran上),这两种包之间有什么区别?

    另一个包,rcppBand可用,在R伪造,声称采取不同的方法来绑定C++和R(我不知道告诉)。

    在CLAN上可供使用的包,声明允许内联C/C++ +,我不确定这与内置函数不同,除了允许代码为内嵌W/R。

    最后,似乎是 in the wild 但目前尚不清楚它是如何得到支持的,因为 author's page 好几年没有更新了。

    我的问题是,这些不同方法的相对优点是什么。它们是最可移植和最健壮的,最容易实现。如果你打算在cran上发布一个包,你会使用哪种方法?

    1 回复  |  直到 10 年前
        1
  •  17
  •   Dirk is no longer here    10 年前

    首先,免责声明:我使用 Rcpp 总是。事实上,当RppTemplate(在RCPP中被重新命名)已经被孤立并且两年没有更新时,我开始用它的初始名称RCPP(在这个名称下它被贡献给 RQuantLib )大约一年前,我做了一些增量更改,您可以在变更日志中找到这些更改。

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

    Rcppbind 每次我检查的时候都不是很积极。Whit Armstrong还具有一个模板化的接口包,名为 rabstraction .

    Inline 是完全不同的:它通过“嵌入”一个r字符串来简化编译/链接循环,然后编译、链接和加载程序。我已经和Oleg谈过拥有内联支持RCPP,这会很好。

    Swig 也很有趣。JoeWang在那里做的很好,为R打包了所有的Quantlib,但是当我上次尝试它时,由于R内部的一些变化,它不再工作了。据Swig团队的人说,Joe可能还在努力。无论如何,swig的目标是更大的库。这个项目可能需要复兴,但也并非没有技术挑战。

    再提一次 RInside 它与Rcpp一起工作,并让你在C++应用程序中嵌入R。

    综上所述: RCPP 对我来说效果很好,特别是对于那些只想添加一两个函数的小型探索性项目。它的焦点是易用性,它允许你“隐藏”一些R的内部结构,这些内部结构并不总是有趣的工作。我认识一些其他用户,我通过电子邮件帮助他们上下。所以我会说去做这个。

    我的“带R的HPC简介”教程中有一些RCPP、RINSide和内联的例子。

    编辑: 因此,让我们来看一个具体的例子(摘自“HPC with R intro”幻灯片,并借用了从Venables和Ripley那里得到的Stephen Milborrow)。任务是枚举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(大约5年后):

    所以这个答案得到了一个上票,所以在我的队列中冒泡了。一 许多 自从我写了它,时间已经过去了,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 类型
    • 由自动添加的异常处理包装 源代码() 代码生成器