Rcpp फ़ंक्शन से एक `new` ऑब्जेक्ट को एक पॉइंटर वापस करने का उचित तरीका


9

1 पर विचार करें) एक संभावित बड़े मेमोरी प्रिंट के साथ एक कस्टम क्लास, और 2) एक शीर्ष-स्तरीय फ़ंक्शन जो कुछ पूर्व-प्रसंस्करण करता है, फिर हमारे कस्टम क्लास का एक नया ऑब्जेक्ट बनाता है और वापस करता है। मूल्य द्वारा अनावश्यक नकल से बचने के लिए, फ़ंक्शन ऑब्जेक्ट को आवंटित करता है और इसके बजाय एक पॉइंटर लौटाता है।

पिछली चर्चा के आधार पर , ऐसा लगता है कि एक नव-निर्मित वस्तु के लिए एक सूचक को वापस करने का उचित तरीका इसके साथ लपेटना है Rcpp::XPtr<>। हालांकि, आर फिर इसे प्रभावी ढंग से के रूप में देखता externalptrहै, और मैं आधुनिक के साथ कास्ट करने के लिए उचित रास्ता खोजने के लिए संघर्ष कर रहा हूँ RCPP_EXPOSED_CLASSऔर RCPP_MODULEकाम करने के रास्ते।

विकल्प कच्चे सूचक को वापस करना है। लेकिन फिर मैं 100% निश्चित नहीं हूं कि ऑब्जेक्ट मेमोरी ठीक से साफ हो जाती है। मैं valgrindस्मृति लीक के लिए परीक्षण करने के लिए दौड़ा , और यह किसी भी नहीं मिला। हालाँकि, सफाई कौन करता है? आर?

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<>रिटर्निंग पॉइंट्स का उचित तरीका है, और यदि हां, तो मुझे परिणाम के रूप में देखने के लिए आर कैसे मिलेगा Double, नहीं externalptr? वैकल्पिक रूप से, यदि एक कच्चे पॉइंटर को वापस करने से मेमोरी की समस्या पैदा नहीं होती है, तो फ़ंक्शन बनाने वाली वस्तु को कौन साफ ​​करता है?


हां, आप शायद Rcpp::XPtrC ++ कोड से एक बाहरी पॉइंटर बनाना चाहते हैं । और आप इसे करना चाहते हैं double *या आपका पेलोड जो भी हो। यहाँ गैलरी में, GitHub में उदाहरण होने चाहिए ... शायद एक प्रेरित खोज के साथ आप कुछ काफी करीब पा सकते हैं?
डिर्क एडडेलबेटेल

हाय @DirkEddelbuettel कलाकारों को वास्तव में होना चाहिए CustomClass*। असली एप्लिकेशन एक कस्टम डेटा संरचना है जिसमें कोई आर समतुल्य नहीं है और सभी इंटरैक्शन ए द्वारा उजागर कार्यक्षमता के माध्यम से किए जाते हैं RCPP_MODULE। मेरे द्वारा खोजा गया निकटतम मैच 7 साल पहले की एक पोस्ट थी , जहाँ ऐसा लगता है कि मुझे एक template <> CustomClass* as()कन्वर्टर को परिभाषित करने की आवश्यकता है । हालाँकि, मैं इस बारे में स्पष्ट नहीं हूँ कि इसे किस तरह से बातचीत करनी चाहिए RCPP_MODULEऔर RCPP_EXPOSED_CLASS, खासकर जब से मुझे लगा कि उत्तरार्द्ध पहले से परिभाषित है wrap()और as()
आर्टेम सोकोलोव

उसी धागे से रोमैन की पोस्ट बहुत मददगार भी है, लेकिन दुर्भाग्य से यह पॉइंटर्स से निपटने के बजाय सीधे वस्तुओं के उपयोग को उजागर करता है।
सोकोलोव

1
मुझे पता है कि मैंने ऐसा ही सामान किया है, लेकिन अब मुझे यकीन नहीं है कि यह सबसे अच्छा उदाहरण है। आप स्पष्ट रूप से एक 'सिंगलटन' ऑब्जेक्ट को सेट कर सकते हैं और एक मॉड्यूल (RcppRedis) के रूप में लपेट सकते हैं; मुझे लगता है कि मैंने वह किया है जो आप एक पूर्व नौकरी या दो पर वर्णन करते हैं लेकिन मैं अब एक अच्छे सार्वजनिक उदाहरण के बारे में नहीं सोच सकता। फिर फिर से - विभिन्न डेटाबेस रैपर और एक्सेस पैकेज ऐसा करते हैं। सबसे छोटा विषय नहीं है, इसलिए शायद एक खिलौना / नकली कार्यान्वयन के साथ शुरू करें और वहां से निर्माण करें?
डिर्क एडल्डबुलेटेल

का उपयोग करना RCPP_EXPOSED_CLASSऔर RCPP_MODULEवास्तव में इसे करने का तरीका है? मैंने पहले कभी ऐसा इस्तेमाल या देखा नहीं है।
एफ। प्रिवी

जवाबों:


7

मुझे लगता है कि यह अलग-अलग दृष्टिकोणों को अलग-अलग देखने के लिए समझ में आता है। इससे भेद स्पष्ट हो जाता है। ध्यान दें कि यह आरसीपीपी मॉड्यूल में चर्चा के समान है।

जब 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 में ऑब्जेक्ट केवल एक "पॉइंटर" है। यदि आप कुछ अच्छा चाहते हैं, तो आप R4 पर S4 / RC / R6 / ... वर्ग जोड़ सकते हैं।

आर साइड पर एक वर्ग में बाहरी पॉइंटर को लपेटना आरसीपी मॉड्यूल का उपयोग करके आपको मुफ्त में मिलने वाला कुछ है:

#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 ++ में एक कंस्ट्रक्टर के बजाय एक फैक्ट्री विधि का उपयोग करने के लिए भी समर्थित है लेकिन आर साइड पर समान उपयोग के साथ:

#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 Modules के साथ R साइड फैक्ट्री फ़ंक्शन को जोड़ना चाहते हैं, तो यह काम में आता है, क्योंकि यह R और C ++ के बीच ऑब्जेक्ट्स को वापस पास करने के लिए आवश्यक एक्सटेंशन Rcpp::asऔर Rcpp::wrapएक्सटेंशन बनाता है । कारखाने का निर्यात उस तरह से किया जा सकता है functionजैसा आपने आरसीपी विशेषताओं का उपयोग करके किया था, जो मुझे अधिक स्वाभाविक लगता है:

#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
बहुत अच्छा सामान। आप यहां एक रोल पर हैं।
डिर्क एडल्डबुलेटेल

धन्यवाद। यह बेहद मददगार है! मुझे लगता factoryहै कि मैं गायब हो गया प्रमुख कनेक्टर टुकड़ा है।
सोकोलोव

एक छोटे से अनुवर्ती के रूप में, क्या आपको पता है कि क्या functionएक फाइनल भी पंजीकृत करता है, या यह केवल है factory ?
आर्टेम सोकोलोव

1
@ArtemSokolov AFAIK डिफाल्टर को कॉल करने वाले डिफ़ॉल्ट फाइनलर द्वारा उत्पन्न होता है class_<T>और इस बात से स्वतंत्र होता है कि ऑब्जेक्ट कैसे बनाया जाता है।
राल्फ स्टनर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.