Betrachten Sie 1) eine benutzerdefinierte Klasse mit einem potenziell großen Speicherdruck und 2) eine Funktion der obersten Ebene, die eine Vorverarbeitung durchführt und dann ein neues Objekt unserer benutzerdefinierten Klasse erstellt und zurückgibt. Um unnötiges Kopieren nach Wert zu vermeiden, weist die Funktion das Objekt zu und gibt stattdessen einen Zeiger darauf zurück.
Basierend auf einer früheren Diskussion scheint es, dass der richtige Weg, einen Zeiger auf ein neu erstelltes Objekt zurückzugeben, darin besteht, es mit einem Wrap zu versehen Rcpp::XPtr<>
. R sieht es dann jedoch effektiv als externalptr
, und ich kämpfe darum, den richtigen Weg zu finden, um es mit der Moderne RCPP_EXPOSED_CLASS
und der RCPP_MODULE
Art und Weise, Dinge zu tun, zu besetzen.
Die Alternative besteht darin, den Rohzeiger zurückzugeben. Aber dann bin ich nicht 100% sicher, dass der Objektspeicher richtig bereinigt wird. Ich lief valgrind
, um auf Speicherlecks zu testen, und es wurden keine gefunden. Doch wer räumt auf? 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")
;
}
In 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
Meine Frage ist, ob Rcpp::Xptr<>
es der richtige Weg ist, Zeiger zurückzugeben, und wenn ja, wie kann ich R dazu bringen, das Ergebnis als Double
nicht zu sehen externalptr
? Wenn das Zurückgeben eines Rohzeigers keine Speicherprobleme verursacht, wer bereinigt dann das von der Funktion erstellte Objekt?
Rcpp::XPtr
einen externen Zeiger aus C ++ - Code erstellen. Und du willst es machendouble *
oder was auch immer deine Nutzlast ist. Hier, in der Galerie, bei GitHub sollte es Beispiele geben ... Vielleicht können Sie mit einer motivierten Suche etwas finden, das nah genug ist?CustomClass*
. Die eigentliche Anwendung ist eine benutzerdefinierte Datenstruktur ohne R-Äquivalent, und alle Interaktionen werden über die von derRCPP_MODULE
. Die engste Übereinstimmung, die meine motivierte Suche gefunden hat, war ein Beitrag von vor 7 Jahren , in dem ich anscheinend einentemplate <> CustomClass* as()
Konverter definieren muss . Ich bin mir jedoch nicht sicher, wie es mitRCPP_MODULE
und interagieren sollRCPP_EXPOSED_CLASS
, zumal ich dachte, dass letzteres bereits definiert istwrap()
undas()
.RCPP_EXPOSED_CLASS
undRCPP_MODULE
ist wirklich der Weg, um es zu tun? Ich habe das noch nie benutzt oder gesehen.Antworten:
Ich halte es für sinnvoll, die verschiedenen Ansätze getrennt zu betrachten. Dies macht die Unterscheidung klarer. Beachten Sie, dass dies der Diskussion in der Rcpp-Modul-Vignette ziemlich ähnlich ist.
Bei Verwendung haben
Rcpp::XPtr
Sie Ihre Klasse und stellen exportierte C ++ - Funktionen für jede Methode bereit, die Sie verfügbar machen möchten:Ausgabe:
Beachten Sie, dass das Objekt in R nur ein "Zeiger" ist. Sie können eine S4 / RC / R6 / ... Klasse auf der R-Seite hinzufügen, wenn Sie etwas Schöneres wollen.
Das Umschließen des externen Zeigers in eine Klasse auf der R-Seite erhalten Sie kostenlos mithilfe von Rcpp-Modulen:
Ausgabe:
Es wird auch unterstützt, eine Factory-Methode anstelle eines Konstruktors in C ++ zu verwenden, jedoch mit identischer Verwendung auf der R-Seite:
Ausgabe:
Schließlich
RCPP_EXPOSED_CLASS
ist praktisch , wenn Sie eine R - Seite Werksfunktion mit RCPP Module kombinieren möchten, da dies die erzeugtRcpp::as
undRcpp::wrap
benötigt Erweiterungen Objekte eine weiter passieren zwischen R und C ++ unterstützen . Die Fabrik könntefunction
wie Sie oder mithilfe von Rcpp-Attributen exportiert werden , was ich natürlicher finde:Ausgabe:
In Bezug auf die Bereinigung: Sowohl
Rcpp::XPtr
als auch das Rcpp-Modul registrieren einen Standard-Finalizer, der den Destruktor des Objekts aufruft. Bei Bedarf können Sie auch einen benutzerdefinierten Finalizer hinzufügen.Ich finde es schwierig, eine Empfehlung für einen dieser Ansätze abzugeben. Vielleicht ist es am besten, jeden von ihnen an einem einfachen Beispiel auszuprobieren und zu sehen, was Sie natürlicher finden.
quelle
factory
ist das Schlüsselverbindungsstück, das ich vermisst habe.function
auch ein Finalizer registriert wird, oder ist es nur sofactory
?class_<T>
und ist unabhängig davon, wie das Objekt erstellt wird.