16

R で C++ ライブラリを利用する最良の方法は何ですか。できれば C++ データ構造を保持します。私は C++ のユーザーではないので、利用可能なアプローチの相対的なメリットについては明確ではありません。R-ext のマニュアルでは、すべての C++ 関数を C でラップすることを提案しているようです。ただし、C++ を組み込む他の手段が少なくとも 4 つまたは 5 つ存在します。

Rcpp (多作のオーバーフロー者 Dirk Eddelbuettel によって維持されている) と RcppTemplate パッケージ (両方とも CRAN 上) の 2 つの方法は、似た系統のパッケージですが、2 つの違いは何ですか?

R forge で利用可能な別のパッケージ rcppbind は、C++ と R をバインドするために異なるアプローチを取ると主張しています (私にはわかりません)。

CRAN で利用可能なパッケージ inline は、インライン C/C++ を許可すると主張しています。コードを R でインライン化できることを除けば、これが組み込み機能と異なるかどうかはわかりません。

そして最後に RSwig が登場しましたが、作者のページが何年も更新されていないため、どの程度サポートされているかは不明です。

私の質問は、これらの異なるアプローチの相対的なメリットは何かということです。どちらが最も移植性が高く堅牢で、最も実装が簡単です。CRAN でパッケージを配布することを計画している場合、どの方法を使用しますか?

4

1 に答える 1

18

まず、免責事項: 私は常にRcppを使用しています。実際、(当時は Rcpp から名前が変更されていた) RcppTemplate がすでに孤立しており、2 年間更新されていなかったとき、私はそれを Rcpp という最初の名前で維持し始めました (この名前でRQuantLibに貢献していました)。それは約 1 年前のことで、ChangeLog に記録されているいくつかの段階的な変更を行いました。

現在、RcppTemplate は、まったく更新も修正もされていない状態で 35 か月が経過した後、ごく最近になって復活しました。興味深い新しいコードが含まれていますが、下位互換性がないように見えるため、既に Rcpp を使用していた場所では使用しません。

私がチェックしたときはいつでも、 rcppbindはあまり積極的に維持されていませんでした。Whit Armstrong には、rabstractionと呼ばれるテンプレート化されたインターフェイス パッケージもあります。

インラインはまったく異なるものです。プログラムを R 文字列として「埋め込む」ことにより、コンパイル/リンク サイクルを緩和し、コンパイル、リンク、およびロードを行います。私は、Rcpp をインラインでサポートすることについて Oleg と話しました。

Swigも面白い。Joe Wang はそこで素晴らしい仕事をし、R のすべての QuantLib をラップしました。Swig チームの誰かによると、Joe はまだそれに取り組んでいるかもしれません。とにかく、Swig の目標はより大きなライブラリです。このプロジェクトはおそらく復活する可能性がありますが、技術的な課題がないわけではありません。

もう 1 つの言及は、Rcppで動作し、R を C++ アプリケーション内に埋め込むことができる RInside に行く必要があります。

要約すると、Rcpp は私にとってはうまく機能します。特に、関数を 1 つまたは 2 つ追加するだけの小規模な探索的プロジェクトの場合は特にそうです。使いやすさに重点を置いており、R の内部構造の一部を「隠す」ことができます。私は、電子メールで何度も何度も助けてきた他の多くのユーザーを知っています。だから私はこれに行くと言うでしょう。

私の「R を使用した HPC の紹介」チュートリアルには、Rcpp、RInside、およびインラインの例がいくつかあります。

編集: それでは、具体的な例を見てみましょう (「HPC with R Intro」スライドから取得し、Venables と Ripley から取得した Stephen Milborrow から借用)。このタスクは、各位置に 1 桁のみを含む 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;
}

これをたとえば名前を付けて保存し、Rcppがインストールされdd.rcpp.cppている場合は、単純に次のように使用します。

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タイプ
  • sourceCpp()コード ジェネレーターによって自動的に追加される例外処理ラッパー
于 2009-11-12T17:55:30.087 に答える