วิธีที่เหมาะสมในการคืนพอยน์เตอร์ไปยังวัตถุ“ ใหม่” จากฟังก์ชั่น Rcpp


9

ลองพิจารณา 1) คลาสที่กำหนดเองที่มีการพิมพ์หน่วยความจำขนาดใหญ่และ 2) ฟังก์ชั่นระดับสูงสุดที่ดำเนินการประมวลผลล่วงหน้าจากนั้นสร้างและส่งคืนออบเจ็กต์ใหม่ของคลาสที่กำหนดเองของเรา เพื่อหลีกเลี่ยงการคัดลอกโดยไม่จำเป็นค่าฟังก์ชั่นจัดสรรวัตถุและส่งกลับตัวชี้ไปที่มันแทน

อยู่บนพื้นฐานของการอภิปรายก่อนหน้านี้Rcpp::XPtr<>ดูเหมือนว่าวิธีการที่เหมาะสมที่จะกลับตัวชี้ไปยังวัตถุที่สร้างขึ้นใหม่คือการห่อมันด้วย อย่างไรก็ตามจากนั้น R ก็เห็นว่าเป็นไปอย่างมีประสิทธิภาพexternalptrและฉันพยายามดิ้นรนเพื่อหาวิธีที่เหมาะสมในการคัดเลือกด้วยวิธีการที่ทันสมัยRCPP_EXPOSED_CLASSและRCPP_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")
    ;
}

ในอาร์

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อย่างไร อีกวิธีหนึ่งถ้าส่งกลับตัวชี้แบบ raw ไม่ทำให้เกิดปัญหาหน่วยความจำใครล้างข้อมูลวัตถุที่ฟังก์ชันสร้างขึ้น


ใช่คุณอาจต้องการRcpp::XPtrสร้างตัวชี้ภายนอกจากรหัส C ++ และคุณต้องการที่จะโยนมันทำdouble *หรือสิ่งที่คุณโหลด ควรมีตัวอย่างที่นี่ที่แกลลอรี่ที่ GitHub ... บางทีด้วยการค้นหาที่มีแรงบันดาลใจคุณสามารถค้นหาบางสิ่งที่อยู่ใกล้พอ
Dirk Eddelbuettel

สวัสดี @DirkEddelbuettel นักแสดงต้องได้รับCustomClass*จริงๆ การประยุกต์ใช้จริงเป็นโครงสร้างข้อมูลที่กำหนดเองที่ไม่มี R RCPP_MODULEเทียบเท่าและปฏิสัมพันธ์ทั้งหมดจะทำผ่านการทำงานเปิดเผยโดย การจับคู่ที่ใกล้เคียงที่สุดที่ฉันค้นพบซึ่งเป็นแรงบันดาลใจคือโพสต์เมื่อ 7 ปีที่แล้วซึ่งดูเหมือนว่าฉันต้องกำหนดตัวtemplate <> CustomClass* as()แปลง แต่ผมไม่มีความชัดเจนเกี่ยวกับวิธีการที่มันควรจะโต้ตอบกับRCPP_MODULEและRCPP_EXPOSED_CLASSโดยเฉพาะอย่างยิ่งนับตั้งแต่ฉันคิดว่าหลังกำหนดไว้แล้วและwrap() as()
Artem Sokolov

โพสต์ของ Romain จากเธรดเดียวกันนั้นก็มีประโยชน์มากเช่นกัน แต่น่าเสียดายที่มันเน้นการใช้งานวัตถุโดยตรงแทนที่จะจัดการกับพอยน์เตอร์
Artem Sokolov

1
ฉันรู้ว่าฉันได้ทำสิ่งที่คล้ายกัน แต่ตอนนี้ฉันไม่แน่ใจว่าสิ่งนี้เป็นตัวอย่างที่ดีที่สุด คุณสามารถตั้งค่าวัตถุ 'ซิงเกิล' อย่างชัดเจนและตัดเป็นโมดูล (RcppRedis); ฉันคิดว่าฉันได้ทำสิ่งที่คุณอธิบายในงานก่อนหน้านี้หรือสองครั้ง แต่ตอนนี้ฉันไม่สามารถนึกถึงตัวอย่างสาธารณะที่ดีได้ จากนั้นอีกครั้ง - ตัวห่อฐานข้อมูลและแพคเกจการเข้าถึงต่างๆทำเช่นนั้น ไม่ใช่หัวข้อที่เล็กที่สุดดังนั้นอาจเริ่มจากการนำของเล่น / จำลองไปใช้และสร้างจากที่นั่น
Dirk Eddelbuettel

การใช้RCPP_EXPOSED_CLASSและRCPP_MODULEเป็นวิธีที่จะทำจริง ๆ หรือไม่ ฉันไม่เคยใช้หรือเห็นสิ่งนั้นมาก่อน
F. Privé

คำตอบ:


7

ฉันคิดว่ามันสมเหตุสมผลที่จะดูวิธีการต่าง ๆ แยกจากกัน สิ่งนี้ทำให้ความแตกต่างชัดเจนขึ้น โปรดทราบว่านี่ค่อนข้างคล้ายกับการสนทนาในบทความของโมดูล Rcpp

เมื่อใช้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 วัตถุเป็นเพียง "ตัวชี้" คุณสามารถเพิ่มคลาส S4 / RC / R6 / ... ทางด้าน R หากคุณต้องการบางสิ่งที่ดีกว่า

การตัดตัวชี้ภายนอกไปยังคลาสที่ด้าน 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ก็มีประโยชน์ถ้าคุณต้องการรวมฟังก์ชั่นโรงงานด้าน R กับโมดูล Rcpp เนื่องจากสิ่งนี้จะสร้าง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 ลงทะเบียน finalizer เริ่มต้นที่เรียกว่าตัวทำลายวัตถุ คุณยังสามารถเพิ่มเครื่องมือกำหนดเองขั้นสุดท้ายได้หากต้องการ

ฉันพบว่ามันยากที่จะให้คำแนะนำสำหรับหนึ่งในวิธีการเหล่านี้ อาจเป็นการดีที่สุดที่จะลองใช้แต่ละตัวอย่างในแบบง่าย ๆ และดูว่าคุณใช้อะไรเป็นธรรมชาติมากกว่านี้


2
สิ่งที่ดีมาก คุณอยู่ที่นี่
Dirk Eddelbuettel

ขอบคุณ. สิ่งนี้มีประโยชน์มาก! ฉันคิดว่าfactoryเป็นตัวเชื่อมต่อที่สำคัญที่ฉันพลาดไป
Artem Sokolov

ในฐานะที่เป็นเพียงการติดตามเล็ก ๆ น้อย ๆ คุณรู้หรือไม่ว่าfunctionยังลงทะเบียนผู้เข้ารอบสุดท้ายหรือfactory ไม่?
Artem Sokolov

1
@ArtemSokolov AFAIK เป็นเครื่องมือเริ่มต้นสุดท้ายที่เรียกว่า destructor นั้นถูกสร้างขึ้นclass_<T>และไม่ขึ้นอยู่กับว่าวัตถุนั้นถูกสร้างขึ้นอย่างไร
Ralf Stubner
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.