Rcpp関数から「新しい」オブジェクトへのポインターを返す適切な方法


9

1)潜在的に大きなメモリプリントを含むカスタムクラス、および2)前処理を実行してからカスタムクラスの新しいオブジェクトを作成して返すトップレベルの関数を検討してください。値による不要なコピーを回避するために、関数はオブジェクトを割り当て、代わりにオブジェクトへのポインターを返します。

以前の議論に基づいて、新しく作成されたオブジェクトへのポインタを返す適切な方法は、それをでラップすることRcpp::XPtr<>です。しかし、Rはそれを効果的にと見なしexternalptr、私はそれをモダンRCPP_EXPOSED_CLASSRCPP_MODULE物事のやり方にキャストする適切な方法を見つけるのに苦労しています。

別の方法は、生のポインタを返すことです。しかし、オブジェクトメモリが適切にクリーンアップされるかどうかは100%確実ではありません。私はvalgrindメモリリークをテストするために実行しましたが、何も見つかりませんでした。しかし、誰が片付けをしますか?R?

test.cpp

#include <Rcpp.h>

// Custom class
class Double {
public:
  Double( double v ) : value(v) {}
  double square() {return value*value;}
private:
  double value;
};

// Make the class visible
RCPP_EXPOSED_CLASS(Double)

// Option 1: returning raw pointer
Double* makeDouble( double x ) {
  Double* pd = new Double(x);
  return pd;
}

// Option 2: returning XPtr<>
SEXP makeDouble2( double x ) {
  Double* pd = new Double(x);
  Rcpp::XPtr<Double> ptr(pd);
  return ptr;
}

RCPP_MODULE(double_cpp) {
  using namespace Rcpp;

  function( "makeDouble", &makeDouble );
  function( "makeDouble2", &makeDouble2 );

  class_<Double>("Double")
    .constructor<double>("Wraps a double")
    .method("square", &Double::square, "square of value")
    ;
}

Rで

Rcpp::sourceCpp("test.cpp")
d1 <- makeDouble(5.4)     # <-- who cleans this up???
# C++ object <0x56257d628e70> of class 'Double' <0x56257c69cf90>
d1$square()
# 29.16

d2 <- makeDouble2(2.3)
# <pointer: 0x56257d3c3cd0>
d2$square()
# Error in d2$square : object of type 'externalptr' is not subsettable

私の質問はRcpp::Xptr<>、ポインタを返す適切な方法であるかどうかであり、そうである場合、Rに結果をDoubleではなく、として表示させるにはどうすればよいexternalptrですか?あるいは、生のポインタを返すことでメモリの問題が発生しない場合、関数が作成したオブジェクトを誰がクリーンアップしますか?


はい、おそらくRcpp::XPtrC ++コードから外部ポインターを作成する必要があります。そして、あなたはそれをキャストしたい、double *またはあなたのペイロードが何であれ。ここ、Gallery、GitHubに例があるはずです...やる気のある検索で、十分に近いものを見つけることができますか?
Dirk Eddelbuettel

こんにちは@DirkEddelbuettelキャストは本当にする必要がありますCustomClass*。実際のアプリケーションは、Rに相当するものがないカスタムデータ構造であり、すべての対話はによって公開されている機能を介して行われRCPP_MODULEます。やる気のある検索で見つかった最も近い一致は7年前の投稿でtemplate <> CustomClass* as()コンバーターを定義する必要があるようです。しかし、私はそれが対話する方法に不明瞭だRCPP_MODULERCPP_EXPOSED_CLASS、私は後者がすでに定義されたと思った、特に以来wrap()as()
Artem Sokolov

同じスレッドからのRomainの投稿も非常に役立ちますが、残念ながら、ポインターの処理ではなく、オブジェクトの使用法を直接強調しています。
Artem Sokolov

1
私は同じようなことをしたことを知っていますが、今これが最も良い例が何であるかわかりません。'singleton'オブジェクトを明確にセットアップし、モジュール(RcppRedis)としてラップできます。先の仕事であなたが説明したことは私がやったと思いますが、今のところ良い例は考えられません。次に、さまざまなデータベースラッパーとアクセスパッケージがそれを行います。最小のトピックではないので、おそらくおもちゃ/モックの実装から始めて、そこからビルドしますか?
Dirk Eddelbuettel

使用RCPP_EXPOSED_CLASSRCPP_MODULEて本当にそれを行う方法ですか?これまでに使用したことも見たこともありません。
F.プリヴェ

回答:


7

異なるアプローチを別々に見ることは理にかなっていると思います。これにより、区別が明確になります。これは、Rcpp Modulesビネットでの説明と非常に似ていることに注意してください。

使用Rcpp::XPtrする場合、クラスがあり、公開するすべてのメソッドにエクスポートされたC ++関数を提供します。

#include <Rcpp.h>

// Custom class
class Double {
public:
    Double( double v ) : value(v) {}
    double square() {return value*value;}
private:
    double value;
};

// [[Rcpp::export]]
Rcpp::XPtr<Double> makeDouble(double x) {
    Double* pd = new Double(x);
    Rcpp::XPtr<Double> ptr(pd);
    return ptr;
}

// [[Rcpp::export]]
double squareDouble(Rcpp::XPtr<Double> x) {
    return x.get()->square();
}

/***R
(d2 <- makeDouble(5.4))
squareDouble(d2)
*/

出力:

> Rcpp::sourceCpp('59384221/xptr.cpp')

> (d2 <- makeDouble(5.4))
<pointer: 0x560366699b50>

> squareDouble(d2)
[1] 29.16

Rではオブジェクトは「ポインタ」にすぎないことに注意してください。もっと良いものが欲しいなら、R側にS4 / RC / R6 / ...クラスを追加できます。

R側のクラスに外部ポインターをラップすることは、Rcppモジュールを使用して無料で取得できるものです。

#include <Rcpp.h>

// Custom class
class Double {
public:
    Double( double v ) : value(v) {}
    double square() {return value*value;}
private:
    double value;
};

RCPP_MODULE(double_cpp) {
    using namespace Rcpp;

    class_<Double>("Double")
        .constructor<double>("Wraps a double")
        .method("square", &Double::square, "square of value")
    ;
}

/***R
(d1 <- new(Double, 5.4))
d1$square()
*/

出力:

> Rcpp::sourceCpp('59384221/modules.cpp')

> (d1 <- new(Double, 5.4))
C++ object <0x560366452eb0> of class 'Double' <0x56036480f320>

> d1$square()
[1] 29.16

C ++ではコンストラクタの代わりにファクトリメソッドを使用することもサポートされていますが、R側でも同じように使用されます。

#include <Rcpp.h>

// Custom class
class Double {
public:
    Double( double v ) : value(v) {}
    double square() {return value*value;}
private:
    double value;
};

Double* makeDouble( double x ) {
    Double* pd = new Double(x);
    return pd;
}

RCPP_MODULE(double_cpp) {
    using namespace Rcpp;

    class_<Double>("Double")
        .factory<double>(makeDouble, "Wraps a double")
        .method("square", &Double::square, "square of value")
    ;
}

/***R
(d1 <- new(Double, 5.4))
d1$square()
*/

出力:

> Rcpp::sourceCpp('59384221/modules-factory.cpp')

> (d1 <- new(Double, 5.4))
C++ object <0x5603665aab80> of class 'Double' <0x5603666eaae0>

> d1$square()
[1] 29.16

最後に、RCPP_EXPOSED_CLASSあなたはこれを作成するので、RcppモジュールとR側の工場の機能を結合したい場合に便利ですRcpp::asし、Rcpp::wrapオブジェクトはRとC ++の間で前後を返すために必要な拡張を。ファクトリはfunction、以前と同じように、またはRcpp属性を使用してエクスポートできます。

#include <Rcpp.h>

// Custom class
class Double {
public:
    Double( double v ) : value(v) {}
    double square() {return value*value;}
private:
    double value;
};

// Make the class visible
RCPP_EXPOSED_CLASS(Double)

// [[Rcpp::export]]
Double makeDouble( double x ) {
    Double d(x);
    return d;
}

RCPP_MODULE(double_cpp) {
    using namespace Rcpp;

    class_<Double>("Double")
        .method("square", &Double::square, "square of value")
    ;
}

/***R
(d1 <- makeDouble(5.4))
d1$square()
*/

出力:

> Rcpp::sourceCpp('59384221/modules-expose.cpp')

> (d1 <- makeDouble(5.4))
C++ object <0x560366ebee10> of class 'Double' <0x560363d5f440>

> d1$square()
[1] 29.16

クリーンアップについて:Rcpp::XPtrとRcppモジュールの両方が、オブジェクトのデストラクタを呼び出すデフォルトのファイナライザを登録します。必要に応じて、カスタムファイナライザを追加することもできます。

これらのアプローチのいずれかを推奨するのは難しいと思います。たぶん、いくつかの簡単な例でそれらのそれぞれを試して、あなたがより自然に使用できると思うものを見るのが最善でしょう。


2
とてもいいものです。あなたはここでロールしています。
Dirk Eddelbuettel

ありがとうございました。これは非常に役立ちます。私factoryは私が行方不明にしていた重要なコネクター部分だと思います。
Artem Sokolov

小さなフォローアップとして、functionファイナライザも登録しているかどうかを知っていますか、それともそれだけfactory ですか?
Artem Sokolov

1
@ArtemSokolov AFAIKデストラクタを呼び出すデフォルトのファイナライザはによって生成されclass_<T>、オブジェクトの作成方法とは無関係です。
Ralf Stubner、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.