簡體   English   中英

在R包中使用C ++庫

[英]Using C++ libraries in an R package

在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上分發一個包,您會使用哪種方法?

首先,免責聲明:我一直使用Rcpp 事實上,當(已經由RCPP時改名)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;
}

如果將其保存為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()代碼生成器自動添加的異常處理包裝器

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM