c++ - valores - devolver un objeto personalizado desde un método envuelto en Rcpp
return en java (3)
Tengo el siguiente problema con el módulo Rcpp: supongamos que tengo dos clases en un módulo Rcpp
class A {
public:
int x;
};
class B
public:
A get_an_a(){
A an_a();
an_a.x=3;
return an_a;
}
};
RCPP_MODULE(mod){
using namespace Rcpp ;
class_<A>("A")
.constructor()
.property("x",&A::get_x)
;
class_<B>("B)
.constructor()
.method("get_an_A",&get_an_a)
;
}
.
En este momento la compilación falla ya que no sabe qué hacer con el tipo de devolución de A.
Pensé que podría hacer algo con Rcpp :: Xptr, sin embargo, no puedo conectarlo a la estructura S4 que Rcpp generó para la clase A. Obtuve un puntero externo desde el método en R.
¿Hay alguna posibilidad de recuperar un objeto correctamente envuelto de un método de la segunda clase?
Gracias, Thomas
[editar]
De acuerdo con la respuesta de Dirk construí una envoltura que puede crear el objeto S4 envuelto:
template <> SEXP wrap(const A &obj) { // insprired from "make_new_object" from Rcpp/Module.h
Rcpp::XPtr<A> xp( new A(obj), true ) ; // copy and mark as finalizable
Function maker=Environment::Rcpp_namespace()[ "cpp_object_maker"];
return maker ( typeid(A).name() , xp );
}
Aún así, no sé cómo recuperar el objeto como un parámetro para un método / función. Lo siguiente no está funcionando:
template <> A* as( SEXP obj){
Rcpp::List l(obj);
Rcpp::XPtr<A> xp( (SEXP) l[".pointer"] );
return (A*) xp;
}
Entonces, ¿cómo podría obtener el puntero externo al objeto C ++ del objeto S4 proporcionado como SEXP en la lista de parámetros?
Es posible si escribes un contenedor para eso. Aquellos no caen como el maná del cielo, necesitas ayudar al compilador proporcionándolo y luego lo eligen.
Ejemplo simple de RcppBDT:
template <> SEXP wrap(const boost::gregorian::date &d) {
// convert to y/m/d struct
boost::gregorian::date::ymd_type ymd = d.year_month_day();
return Rcpp::wrap(Rcpp::Date( ymd.year, ymd.month, ymd.day ));
}
Aquí, una clase de Boost se convierte en una clase Rcpp (Date) que luego se devuelve (e incluso allí necesitamos un wrap-up explícito). Para clases más complejas necesitas convertidores más complejos.
Editar: Y, por supuesto, debe tener en cuenta que todo lo que se puede llamar de R todavía necesita ajustarse a la interfaz SEXP foo(SEXP a, SEXP b, ...)
básica prescrita por .Call()
.
Ok, creo que lo tengo. Pero no hay garantías. De acuerdo con la respuesta de Dirk construí una envoltura que puede crear el objeto S4 envuelto:
template <> SEXP wrap(const A &obj) { // insprired from "make_new_object" from Rcpp/Module.h
Rcpp::XPtr<A> xp( new A(obj), true ) ; // copy and mark as finalizable
Function maker=Environment::Rcpp_namespace()[ "cpp_object_maker"];
return maker ( typeid(A).name() , xp );
}
Si un método está esperando un objeto como parámetro (aquí un puntero a ese objeto), el siguiente contenedor "como" puede ser útil:
template <> A* as( SEXP obj){
Rcpp::Environment e(obj);
Rcpp::XPtr<A> xp( (SEXP) e.get(".pointer") );
return (A*) xp;
}
por supuesto, también puede devolver * xp, que permite tomar como parámetros parámetros que no sean punteros, pero que de nuevo copia el objeto.
Esta característica se ha agregado en Rcpp 0.10.0
Hubo otras razones por las cuales su código no compiló. El siguiente código funciona para mí:
class A {
public:
A(int x_) : x(x_){}
int x;
};
class B {
public:
A get_an_a(){
A an_a(3);
return an_a;
}
};
RCPP_EXPOSED_CLASS(A)
RCPP_EXPOSED_CLASS(B)
RCPP_MODULE(stackmod){
using namespace Rcpp ;
class_<A>("A")
.constructor<int>()
.field("x",&A::x)
;
class_<B>("B")
.constructor()
.method("get_an_A",&B::get_an_a)
;
}
El precio a pagar es llamar a la macro RCPP_EXPOSED_CLASS
para todas las clases que:
- Uno de tu módulo está exponiendo
- Desea usar como resultado de un método expuesto o como un parámetro
Con esto, obtengo:
> b <- new( B )
> b$get_an_A( )
C++ object <0x10330a9d0> of class ''A'' <0x1006f46e0>
> b$get_an_A( )$x
[1] 3