Considere 1) una clase personalizada con una impresión de memoria potencialmente grande, y 2) una función de nivel superior que realiza un preprocesamiento, luego crea y devuelve un nuevo objeto de nuestra clase personalizada. Para evitar una copia innecesaria por valor, la función asigna el objeto y le devuelve un puntero.
Según una discusión anterior , parece que la forma correcta de devolver un puntero a un objeto recién creado es envolverlo Rcpp::XPtr<>
. Sin embargo, R lo ve efectivamente externalptr
, y estoy luchando por encontrar la forma adecuada de lanzarlo con lo moderno RCPP_EXPOSED_CLASS
y lo moderno.RCPP_MODULE
forma de hacer las cosas.
La alternativa es devolver el puntero sin formato. Pero entonces no estoy 100% seguro de que la memoria del objeto se limpie correctamente. Corrí valgrind
a buscar pérdidas de memoria y no encontré ninguna. Sin embargo, ¿quién hace la limpieza? 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")
;
}
En 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
Mi pregunta es si Rcpp::Xptr<>
es la forma correcta de devolver punteros, y si es así, ¿cómo hago para que R vea el resultado Double
, no externalptr
? Alternativamente, si devolver un puntero sin formato no causa problemas de memoria, ¿quién limpia el objeto que crea la función?
Rcpp::XPtr
crear un puntero externo a partir del código C ++. Y quieres lanzarlodouble *
o lo que sea tu carga útil. Debería haber ejemplos aquí, en la Galería, en GitHub ... ¿Quizás con una búsqueda motivada pueda encontrar algo lo suficientemente cerca?CustomClass*
. La aplicación real es una estructura de datos personalizada sin equivalente de R y todas las interacciones se realizan a través de la funcionalidad expuesta porRCPP_MODULE
. La coincidencia más cercana que encontró mi búsqueda motivada fue una publicación de hace 7 años , donde parece que necesito definir untemplate <> CustomClass* as()
convertidor. Sin embargo, no tengo claro cómo debería interactuarRCPP_MODULE
yRCPP_EXPOSED_CLASS
, especialmente porque pensé que este último ya había definidowrap()
yas()
.RCPP_EXPOSED_CLASS
yRCPP_MODULE
es realmente la forma de hacerlo? Nunca he usado o visto eso antes.Respuestas:
Creo que tiene sentido mirar los diferentes enfoques por separado. Esto hace que la distinción sea más clara. Tenga en cuenta que esto es bastante similar a la discusión en la viñeta Módulos Rcpp.
Al usar
Rcpp::XPtr
, tiene su clase y proporciona funciones de C ++ exportadas para cada método que desea exponer:Salida:
Tenga en cuenta que en R el objeto es solo un "puntero". Puede agregar una clase S4 / RC / R6 / ... en el lado R si desea algo más agradable.
Ajustar el puntero externo en una clase en el lado R es algo que obtienes gratis al usar módulos Rcpp:
Salida:
También se admite el uso de un método de fábrica en lugar de un constructor en C ++ pero con un uso idéntico en el lado R:
Salida:
Finalmente,
RCPP_EXPOSED_CLASS
resulta útil si desea combinar una función de fábrica del lado R con los módulos Rcpp, ya que esto crea las extensionesRcpp::as
yRcpp::wrap
necesarias para pasar los objetos de un lado a otro entre R y C ++. La fábrica se puede exportar a través defunction
como lo hizo o utilizando los atributos de Rcpp, lo que me parece más natural:Salida:
Con respecto a la limpieza: Ambos
Rcpp::XPtr
módulos y Rcpp registran un finalizador predeterminado que llama al destructor del objeto. También puede agregar un finalizador personalizado si es necesario.Me resulta difícil dar una recomendación para uno de estos enfoques. Quizás sea mejor probar cada uno de ellos con un ejemplo simple y ver qué le parece más natural de usar.
fuente
factory
es la pieza clave del conector que me he estado perdiendo.function
también registra un finalizador, o es solofactory
?class_<T>
y es independiente de cómo se crea el objeto.