Rozważ 1) klasę niestandardową z potencjalnie dużym drukiem pamięci oraz 2) funkcję najwyższego poziomu, która wykonuje wstępne przetwarzanie, a następnie tworzy i zwraca nowy obiekt naszej klasy niestandardowej. Aby uniknąć niepotrzebnego kopiowania według wartości, funkcja przydziela obiekt i zwraca do niego wskaźnik.
Na podstawie poprzedniej dyskusji wydaje się, że właściwym sposobem na zwrócenie wskaźnika do nowo utworzonego obiektu jest owinięcie go Rcpp::XPtr<>
. Jednak R następnie widzi to skutecznie jako externalptr
, i staram się znaleźć właściwy sposób, aby rzucić to na nowoczesność RCPP_EXPOSED_CLASS
i RCPP_MODULE
sposób robienia rzeczy.
Alternatywą jest zwrócenie surowego wskaźnika. Ale nie jestem w 100% pewien, że pamięć obiektów zostanie odpowiednio wyczyszczona. Pobiegłem valgrind
sprawdzić, czy nie ma wycieków pamięci, ale nie znalazłem żadnych. Kto jednak sprząta? 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")
;
}
W 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
Moje pytanie brzmi: czy Rcpp::Xptr<>
jest właściwy sposób zwracania wskaźników, a jeśli tak, to w jaki sposób sprawić, aby R zobaczył wynik jako Double
nie externalptr
? Alternatywnie, jeśli zwrócenie surowego wskaźnika nie powoduje problemów z pamięcią, kto czyści obiekt utworzony przez funkcję?
Rcpp::XPtr
utworzyć zewnętrzny wskaźnik z kodu C ++. I chcesz to rzucić,double *
czy cokolwiek innego. Tu powinny być przykłady, w Galerii, na GitHub ... Może dzięki zmotywowanemu wyszukiwaniu możesz znaleźć coś wystarczająco blisko?CustomClass*
. Prawdziwa aplikacja to niestandardowa struktura danych bez odpowiednika R, a wszystkie interakcje odbywają się za pośrednictwem funkcji udostępnionej przezRCPP_MODULE
. Najbliższym dopasowaniem, jakie znalazłem przy wyszukiwaniu motywowanym, był post sprzed 7 lat , w którym wydaje się, że muszę zdefiniowaćtemplate <> CustomClass* as()
konwerter. Nie jestem jednak pewien, w jaki sposób powinien on wchodzić w interakcjeRCPP_MODULE
iRCPP_EXPOSED_CLASS
, zwłaszcza że myślałem, że to drugie zostało już zdefiniowanewrap()
ias()
.RCPP_EXPOSED_CLASS
i czyRCPP_MODULE
to naprawdę sposób, aby to zrobić? Nigdy wcześniej tego nie używałem ani nie widziałem.Odpowiedzi:
Myślę, że sensownie jest osobno spojrzeć na różne podejścia. To czyni rozróżnienie wyraźniejszym. Zauważ, że jest to dość podobne do dyskusji w winiecie Moduły Rcpp.
Podczas korzystania
Rcpp::XPtr
masz klasę i udostępniasz wyeksportowane funkcje C ++ dla każdej metody, którą chcesz udostępnić:Wynik:
Zauważ, że w R obiekt jest tylko „wskaźnikiem”. Możesz dodać klasę S4 / RC / R6 / ... po stronie R, jeśli chcesz czegoś ładniejszego.
Zawijanie zewnętrznego wskaźnika w klasę po stronie R to coś, co otrzymujesz za darmo, używając modułów Rcpp:
Wynik:
Obsługiwane jest również użycie metody fabrycznej zamiast konstruktora w C ++, ale z identycznym użyciem po stronie R:
Wynik:
Wreszcie
RCPP_EXPOSED_CLASS
przydaje się, jeśli chcesz połączyć funkcję fabryczną po stronie R z modułami Rcpp, ponieważ tworzy toRcpp::as
iRcpp::wrap
rozszerzenia potrzebne do przekazywania obiektów z powrotem między R i C ++. Fabrykę można wyeksportowaćfunction
tak jak ty lub używając atrybutów Rcpp, co uważam za bardziej naturalne:Wynik:
Odnośnie czyszczenia: zarówno
Rcpp::XPtr
Moduły, jak i Rcpp rejestrują domyślny finalizator, który wywołuje destruktor obiektu. W razie potrzeby możesz również dodać niestandardowy finalizator.Trudno mi zalecić jedno z tych podejść. Może najlepiej wypróbować każdy z nich na prostym przykładzie i przekonać się, co jest bardziej naturalne w użyciu.
źródło
factory
to kluczowy element złącza, którego mi brakowało.function
rejestruje także finalizator, czy tylko tofactory
?class_<T>
i jest niezależny od sposobu tworzenia obiektu.