2013-08-27 8 views
7

आर में, मैं डेटा को स्टोर करने के लिए आंतरिक C++ संरचना का उपयोग कर रहा हूं (R_ExternalPtr का उपयोग करके)। डेटा को विभिन्न कार्यों का उपयोग करके संसाधित किया जा सकता है।बचत/लोड की अनुमति देने के लिए आर में सी ++ आंतरिक डेटा संरचना को कैसे संभालें?

#include <Rinternals.h> 
class MyObject{ 
     public: 
     int type; 
     MyObject(int t):type(t){} 
}; 


void finalizeMyObject(SEXP ptr){ 
    MyObject * mo= static_cast<MyObject *>(R_ExternalPtrAddr(ptr)); 
    delete mo; 
} 

extern "C" { 

    SEXP CreateObject(SEXP type) { 
     SEXP ans; 
     int nb = length(type); 
     PROTECT(ans=allocVector(VECSXP, nb)); 
     int * typeP = INTEGER(type); 
     SEXP cl; 
     PROTECT(cl = allocVector(STRSXP, 1)); 
     SET_STRING_ELT(cl, 0, mkChar("myObject")); 
     for(int i=0; i<nb; i++){ 
      MyObject * mo = new MyObject(typeP[i]); 
      SEXP tmpObj = R_MakeExternalPtr(mo, R_NilValue, R_NilValue); 
      R_RegisterCFinalizerEx(tmpObj, (R_CFinalizer_t) finalizeMyObject, TRUE); 

      classgets(tmpObj, cl); 
      SET_VECTOR_ELT(ans, i, tmpObj);//Put in vector 
     } 
     UNPROTECT(2); 
     return ans; 
    } 

    SEXP printMyObject(SEXP myObjects){ 
     int nb = length(myObjects); 
     for(int i=0; i<nb; i++){ 
      SEXP moS=VECTOR_ELT(myObjects, i); 
      MyObject * mo= static_cast<MyObject *>(R_ExternalPtrAddr(moS)); 
      Rprintf("%d\n", mo->type); 
     } 
     return R_NilValue; 
    } 
} 

एक बार आंतरिक डेटा संरचना का निर्माण किया जाता है, कई कार्य अलग-अलग आंकड़े की गणना करने के कहा जा सकता है: यहाँ एक सरल उदाहरण (वास्तविक डेटा संरचना और अधिक जटिल है)। उपर्युक्त फ़ंक्शन प्रिंट MyObject ऐसे फ़ंक्शन का उदाहरण प्रदान करता है।

समस्या इस आंतरिक संरचना को बचाने की कोशिश करते समय उत्पन्न होती है। ऐसा लगता है कि सूचक पते को बचाता है। जब वस्तुओं को पुनः लोड किया जाता है, तो हमें एक सेगफॉल्ट मिलता है। क्या सबसे अच्छा तरीका यह सही ढंग से संभाल करने के लिए है: यहाँ एक उदाहरण आर कोड (लगता है कि myobject.cpp ऊपर कोड होता है और myobject.dll को संकलित किया गया था)

dyn.load("myobject.dll") 
## Create the internal data structure 
xx <- .Call("CreateObject", 1:10) 
##Everything is fine 
.Call("printMyObject", xx) 
## Save it, no errors 
save(xx, file="xx.RData") 
## remove all objects 
rm(list=ls()) 
## Load the data 
load(file="xx.RData") 
##segfault 
.Call("printMyObject", xx) 

मेरा प्रश्न है? मैंने कुछ रणनीतियों के बारे में सोचा, लेकिन पहले के अलावा, मुझे नहीं पता कि यह कैसे किया जा सकता है (और यदि यह करना संभव है):

  • आंतरिक ऑब्जेक्ट को वापस न करें। उपयोगकर्ताओं के पास हमेशा आर संरचनाएं होती हैं जो आंतरिक सी ++ संरचना में आंतरिक रूप से परिवर्तित होती हैं (प्रत्येक फ़ंक्शन कॉल के लिए, जैसे प्रिंट और अन्य)। पेशेवर: डेटा segfault के बिना बचाया जा सकता है। विपक्ष: यह बहुत अक्षम है, खासकर जब बहुत सारे डेटा होते हैं।
  • ऑब्जेक्ट को R और सी ++ संरचना के रूप में संग्रहीत करें और पता लगाने का प्रयास करें (मुझे नहीं पता कि अगर C++ संरचना इसे पुनर्निर्माण के लिए दूषित हो जाती है। पेशेवर: आंतरिक डेटा संरचना प्रति सत्र केवल एक बार बनाई गई है। विपक्ष: स्मृति समस्याओं के लिए सबसे अच्छा नहीं (डेटा दो बार संग्रहीत)।
  • केवल सी ++ संरचना का उपयोग करें और कभी भी सहेजें/लोड (वास्तविक समाधान का उपयोग नहीं किया जाता)। समस्या यह है कि कोई त्रुटि संदेश नहीं है, लेकिन केवल एक segfault है।
  • आर सेव/लोड फ़ंक्शन का उपयोग होने पर आंतरिक C++ ऑब्जेक्ट को क्रमबद्ध करने का एक तरीका खोजें।

कोई भी विचार/सुझाव बहुत स्वागत है।

+0

मुझे नहीं लगता कि इस के लिए हुक कर रहे हैं है। मुझे इस समस्या का हल करना अच्छा लगेगा। आरसीपीपी मॉड्यूल बहुत सारे बाहरी पॉइंटर्स का उपयोग करते हैं और वर्तमान में हम नहीं जानते कि दृढ़ता को कैसे संभालना है। –

+0

क्या आपने डीबगर को हुक अप करने का प्रयास किया है यह देखने के लिए कि सी ++ कोड में कौन सी रेखा आपको segfault देता है? लोड से पहले और बाद में संदर्भ (शून्य?) का निरीक्षण करना? यह देखने के लिए ब्रेकपॉइंट सेट करना कि अंतिमकरण कोड कब चलाया जाता है? शायद आप सहेजने के लिए कॉल से पहले स्मृति को मुक्त कर रहे हैं, लेकिन यह अभी भी बरकरार है (समय के लिए) ताकि printMyObject फ़ंक्शन काम पर हो। – cachance7

+0

segfault लाइन 'Rprintf ("% d \ n", mo-> प्रकार) से आता है;' लेकिन केवल दूसरी बार। इसकी अपेक्षा की जाती है, क्योंकि डेटा संरचना सहेजी नहीं जाती है और इस प्रकार बाद में पुनः लोड नहीं होती है। लोड होने के बाद, मो पॉइंटर सही नहीं है, एक segfault –

उत्तर

3

अंत में, मैं सी ++ ऑब्जेक्ट्स (डेटा दृढ़ता) के साथ आर सेव/लोड मैकेनिज्म को सही तरीके से संभालने के लिए 2 समाधान आया। कोई भी सही नहीं है, लेकिन ऐसा कुछ भी करने से बेहतर लगता है। जल्द ही, वे समाधान हैं (विवरण नीचे उपलब्ध हैं):

  • सी ++ ऑब्जेक्ट्स को स्टोर करने के लिए आर कच्चे डेटा प्रकार का उपयोग करना। यह स्मृति कुशल होने का लाभ है, लेकिन यह अभ्यास में काफी जटिल है। निजी तौर पर, मैं इसे एक बड़ी परियोजना के लिए उपयोग नहीं करूँगा, क्योंकि स्मृति प्रबंधन कठिन हो सकता है। निश्चित रूप से, टेम्पलेट वर्गों का उपयोग करके शायद कुछ संभव सुधार हो सकते हैं (यदि आपको कोई विचार है तो कृपया इसे साझा करें)।
  • ऑब्जेक्ट को आर और सी ++ संरचना के रूप में स्टोर करें और पुनः लोड करने के बाद C++ ऑब्जेक्ट्स को पुनर्निर्माण करें। सी ++ ऑब्जेक्ट केवल एक बार बनाया जाता है (प्रत्येक फ़ंक्शन कॉल के लिए नहीं), लेकिन डेटा दो बार संग्रहीत किया जाता है। एक बड़ी परियोजना पर लागू करने के लिए इसका अधिक लाभ होने का गंभीर लाभ है।

उन समाधानों को प्रस्तुत करने के लिए, मैं पहले समाधान की जटिलता को उजागर करने के लिए थोड़ा और जटिल उदाहरण का उपयोग करता हूं। इस उदाहरण में, हमारा उदाहरण सी ++ ऑब्जेक्ट एक प्रकार की लिंक्ड सूची है।

पहले समाधान: उपयोग आर रॉ डेटा

विचार (@KarlForner द्वारा प्रस्तावित) वस्तु की सामग्री को संग्रहीत करने आर कच्चे डेटा प्रकार का प्रयोग है।अभ्यास में इसका अर्थ है:

  • आर कच्चे डेटा प्रकार के साथ स्मृति आवंटित करना।
  • सही मात्रा में स्मृति आवंटित करना सुनिश्चित करें (यह जटिल हो सकता है)।
  • निर्दिष्ट स्मृति पते (कच्चे डेटा वाला एक) पर C++ ऑब्जेक्ट बनाने के लिए नया प्लेसमेंट का उपयोग करें।

    #include <Rinternals.h> 
    #include <new> 
    //Our example Object: A linked list. 
    class MyRawObject{ 
        int type; 
        // Next object in the list. 
        bool hasnext; 
        public: 
         MyRawObject(int t):type(t), hasnext (false){ 
          if(t>1){ 
           //We build a next object. 
           hasnext = true; 
           //Here we use placement new to build the object in the next allocated memory 
           // No need to store a pointer. 
          //We know that this is in the next contiguous location (see memory allocation below) 
          new (this+1) MyRawObject(t-1); 
          } 
         } 
    
         void print(){ 
          Rprintf(" => %d ", type); 
          if(this->hasnext){ 
           //Next object located is in the next contiguous memory block 
           (this+1)->print(); //Next in allocated memory 
          } 
         } 
    }; 
    extern "C" { 
        SEXP CreateRawObject(SEXP type) { 
         SEXP ans; 
         int nb = length(type); 
         PROTECT(ans=allocVector(VECSXP, nb)); 
         int * typeP = INTEGER(type); 
         //When allocating memory, we need to know the size of our object. 
         int MOsize =sizeof(MyRawObject); 
         for(int i=0; i<nb; i++){ 
          SEXP rawData; 
          //Allocate the memory using R RAW data type. 
          //Take care to allocate the right amount of memory 
           //Here we have typeP[i] objects to create. 
          PROTECT(rawData=allocVector(RAWSXP, typeP[i]*MOsize)); 
          //Memory address of the allocated memory 
          unsigned char * buf = RAW(rawData); 
          //Here we use placement new to build the object in the allocated memory 
          new (buf) MyRawObject(typeP[i]); 
          SET_VECTOR_ELT(ans, i, rawData);//Put in vector 
          UNPROTECT(1); 
         } 
         UNPROTECT(1); 
         return ans; 
        } 
        SEXP printRawObject(SEXP myObjects){ 
         int nb = length(myObjects); 
         for(int i=0; i<nb; i++){ 
          SEXP moS=VECTOR_ELT(myObjects, i); 
          //Use reinterpret_cast to have a pointer of type MyRawObject pointing to the RAW data 
          MyRawObject * mo= reinterpret_cast<MyRawObject *>(RAW(moS)); 
          if(mo!=NULL){ 
           Rprintf("Address: %d", mo); 
           mo->print(); 
           Rprintf("\n"); 
          }else{ 
           Rprintf("Null pointer!\n"); 
          } 
         } 
         return R_NilValue; 
        } 
    } 
    

    कार्यों ऐसे ही उदाहरण के लिए आर में सीधे इस्तेमाल किया जा सकता है::

यहाँ फ़ाइल "myobject.cpp" है

## Create the internal data structure 
xx <- .Call("CreateRawObject", 1:10) 
##Print 
.Call("printRawObject", xx) 
## Save it 
save(xx, file="xxRaw.RData") 
## remove all objects 
rm(xx) 
## Load the data 
load(file="xxRaw.RData") 
##Works ! 
.Call("printRawObject", xx) 

इस समाधान के साथ कई मुद्दों कर रहे हैं :

  • विनाशकों को नहीं कहा जाता है (आप जो कर रहे हैं उसके आधार पर मैं एन विनाशक, यह एक मुद्दा हो सकता है)।
  • यदि आपकी कक्षा में अन्य ऑब्जेक्ट में पॉइंटर है, तो सभी पॉइंटर्स को पुनः लोड करने के बाद इसे काम करने के लिए मेमोरी लोकेशन के सापेक्ष होना चाहिए (जैसे लिंक की गई सूची के साथ)।
  • यह काफी जटिल है।
  • मुझे यकीन नहीं है कि सहेजी गई फ़ाइल (.RData) क्रॉस-प्लेटफार्म होगी (क्या कंप्यूटर में आरडीएटा फ़ाइल साझा करना सुरक्षित होगा?), क्योंकि मुझे यकीन नहीं है कि किसी ऑब्जेक्ट को स्टोर करने के लिए उपयोग की जाने वाली मेमोरी होगी सभी प्लेटफॉर्म पर वही।

दूसरा समाधान: ऑब्जेक्ट को आर और सी ++ संरचना के रूप में स्टोर करें और पुनः लोड करने के बाद सी ++ ऑब्जेक्ट्स का पुनर्निर्माण करें।

विचार यह है कि प्रत्येक फंक्शन कॉल में C++ पॉइंटर्स सही हैं या नहीं। यदि नहीं, तो ऑब्जेक्ट का पुनर्निर्माण करें, अन्यथा चरण को अनदेखा करें। यह संभव हो गया है क्योंकि हम सीधे आर ऑब्जेक्ट (सी ++ में) संशोधित कर रहे हैं। इसलिए परिवर्तन सभी आगामी कॉल के लिए प्रभावी होगा। लाभ यह है कि सी ++ ऑब्जेक्ट्स केवल एक बार बनाए जाते हैं (प्रत्येक फ़ंक्शन कॉल के लिए नहीं), लेकिन डेटा दो बार संग्रहीत किया जाता है। एक बड़ी परियोजना पर लागू करने के लिए इसका अधिक लाभ होने का गंभीर लाभ है।

"myobject.cpp" फ़ाइल में।

#include <Rinternals.h> 
//Our object can be made simpler because we can use pointers 
class MyObject{ 
    int type; 
    //Pointer to the next object 
    MyObject *next; 
    public: 
     MyObject(int t):type(t), next(NULL){ 
      if(t>1){ 
       next = new MyObject(t-1); 
      } 
     } 
     ~MyObject(){ 
      if(this->next!=NULL){ 
       delete next; 
      } 
     } 
     void print(){ 
      Rprintf(" => %d ", type); 
      if(this->next!=NULL){ 
       this->next->print(); 
      } 
     } 
}; 
void finalizeMyObject(SEXP ptr){ 
    MyObject * mo= static_cast<MyObject *>(R_ExternalPtrAddr(ptr)); 
    delete mo; 
} 

extern "C" { 

    SEXP CreateObject(SEXP type) { 
     SEXP ans; 
     int nb = length(type); 
     PROTECT(ans=allocVector(VECSXP, nb)); 
     int * typeP = INTEGER(type); 
     SEXP cl; 
     PROTECT(cl = allocVector(STRSXP, 1)); 
     SET_STRING_ELT(cl, 0, mkChar("myObject")); 
     for(int i=0; i<nb; i++){ 
      MyObject * mo = new MyObject(typeP[i]); 
      SEXP tmpObj = R_MakeExternalPtr(mo, R_NilValue, R_NilValue); 
      R_RegisterCFinalizerEx(tmpObj, (R_CFinalizer_t) finalizeMyObject, TRUE); 

      classgets(tmpObj, cl); 
      SET_VECTOR_ELT(ans, i, tmpObj);//Put in vector 
     } 
     UNPROTECT(2); 
     return ans; 
    } 

    SEXP printMyObject(SEXP myObjects){ 
     int nb = length(myObjects); 
     for(int i=0; i<nb; i++){ 
      SEXP moS=VECTOR_ELT(myObjects, i); 
      MyObject * mo= static_cast<MyObject *>(R_ExternalPtrAddr(moS)); 
      if(mo!=NULL){ 
       Rprintf("Address: %d", mo); 
       mo->print(); 
       Rprintf("\n"); 
      }else{ 
       Rprintf("Null pointer!\n"); 
      } 
     } 
     return R_NilValue; 
    } 
    //This function check if a C++ object is NULL, if it is, it rebuilds all the C++ objects. 
    SEXP checkRebuildMyObject(SEXP myObjects, SEXP type){ 
     int nb = length(myObjects); 
     if(nb==0){ 
      return R_NilValue; 
     } 
     if(R_ExternalPtrAddr(VECTOR_ELT(myObjects, 1))){ //Non corrupted ptrs 
      return R_NilValue; 
     } 
     int * typeP = INTEGER(type); 
     SEXP cl; 
     PROTECT(cl = allocVector(STRSXP, 1)); 
     SET_STRING_ELT(cl, 0, mkChar("myObject")); 
     for(int i=0; i<nb; i++){ 
      MyObject * mo = new MyObject(typeP[i]); 
      SEXP tmpObj = R_MakeExternalPtr(mo, R_NilValue, R_NilValue); 
      R_RegisterCFinalizerEx(tmpObj, (R_CFinalizer_t) finalizeMyObject, TRUE); 
      classgets(tmpObj, cl); 
      SET_VECTOR_ELT(myObjects, i, tmpObj);//Put in vector 
     } 
     UNPROTECT(1); 
     return R_NilValue; 
    } 

} 

आर पक्ष में, हम इस प्रकार उन कार्यों का उपयोग कर सकते हैं:

dyn.load("myobject.dll") 

CreateObjectR <- function(type){ 
## We use a custom object type, that store both C++ and R data 
type <- as.integer(type) 
mo <- list(type=type, myObject= .Call("CreateObject", type)) 
class(mo) <- "myObjectList" 
return(mo) 
} 

print.myObjectList <- function(x, ...){ 
## Be sure to check the C++ objects before calling the functions. 
.Call("checkRebuildMyObject", x$myObject, x$type) 
.Call("printMyObject", x$myObject) 
invisible() 
} 

xx <- CreateObjectR(1:10) 
print(xx) 
save(xx, file="xx.RData") 
rm(xx) 
load(file="xx.RData") 
print(xx) 
संबंधित मुद्दे