Considérons 1) une classe personnalisée avec une impression de mémoire potentiellement volumineuse et 2) une fonction de niveau supérieur qui effectue un prétraitement, puis crée et renvoie un nouvel objet de notre classe personnalisée. Pour éviter une copie inutile par valeur, la fonction alloue l'objet et lui renvoie un pointeur à la place.
Sur la base d'une discussion précédente , il semble que la bonne façon de renvoyer un pointeur sur un objet nouvellement créé est de l'envelopper Rcpp::XPtr<>
. Cependant, R le voit alors efficacement externalptr
, et j'ai du mal à trouver la bonne façon de le mouler avec la manière moderne RCPP_EXPOSED_CLASS
et la RCPP_MODULE
façon de faire les choses.
L'alternative est de renvoyer le pointeur brut. Mais je ne suis pas sûr à 100% que la mémoire de l'objet soit correctement nettoyée. J'ai couru valgrind
pour tester les fuites de mémoire et il n'en a pas trouvé. Mais qui fait le nettoyage? 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")
;
}
Dans 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
Ma question est de savoir si Rcpp::Xptr<>
est la bonne façon de renvoyer des pointeurs, et si oui, comment puis-je obtenir R pour voir le résultat Double
, non externalptr
? Alternativement, si le retour d'un pointeur brut ne cause pas de problèmes de mémoire, qui nettoie l'objet créé par la fonction?
Rcpp::XPtr
créer un pointeur externe à partir du code C ++. Et vous voulez le faire fairedouble *
ou quelle que soit votre charge utile. Il devrait y avoir des exemples ici, à la galerie, au GitHub ... Peut-être qu'avec une recherche motivée, vous pouvez trouver quelque chose d'assez proche?CustomClass*
. L'application réelle est une structure de données personnalisée sans équivalent R et toutes les interactions se font via la fonctionnalité exposée par leRCPP_MODULE
. La correspondance la plus proche que ma recherche motivée a trouvée était un article d'il y a 7 ans , où il semble que je doive définir untemplate <> CustomClass* as()
convertisseur. Cependant, je ne sais pas comment il devrait interagir avecRCPP_MODULE
etRCPP_EXPOSED_CLASS
, d'autant plus que je pensais que ce dernier était déjà définiwrap()
etas()
.RCPP_EXPOSED_CLASS
etRCPP_MODULE
est-ce vraiment le moyen de le faire? Je n'ai jamais utilisé ou vu cela auparavant.Réponses:
Je pense qu'il est logique d'examiner les différentes approches séparément. Cela rend la distinction plus claire. Notez que cela est assez similaire à la discussion dans la vignette des modules Rcpp.
Lorsque
Rcpp::XPtr
vous utilisez, vous avez votre classe et fournissez des fonctions C ++ exportées pour chaque méthode que vous souhaitez exposer:Production:
Notez que dans R, l'objet n'est qu'un "pointeur". Vous pouvez ajouter une classe S4 / RC / R6 / ... sur le côté R si vous voulez quelque chose de plus agréable.
Envelopper le pointeur externe dans une classe du côté R est quelque chose que vous obtenez gratuitement en utilisant les modules Rcpp:
Production:
Il est également pris en charge d'utiliser une méthode d'usine au lieu d'un constructeur en C ++ mais avec une utilisation identique du côté R:
Production:
Enfin,
RCPP_EXPOSED_CLASS
est pratique si vous souhaitez combiner une fonction d'usine côté R avec des modules Rcpp, car cela crée les extensionsRcpp::as
etRcpp::wrap
nécessaires pour passer des objets entre R et C ++. L'usine pourrait être exportée viafunction
comme vous l'avez fait ou en utilisant les attributs Rcpp, ce que je trouve plus naturel:Production:
Concernant le nettoyage: les
Rcpp::XPtr
modules Rcpp et Rcpp enregistrent un finaliseur par défaut qui appelle le destructeur de l'objet. Vous pouvez également ajouter un finaliseur personnalisé si nécessaire.J'ai du mal à formuler une recommandation pour l'une de ces approches. Il est peut-être préférable d'essayer chacun d'eux sur un exemple simple et de voir ce que vous trouvez plus naturel à utiliser.
la source
factory
c'est le connecteur clé qui me manquait.function
enregistre également un finaliseur, ou est-ce seulementfactory
?class_<T>
et est indépendant de la façon dont l'objet est créé.