Возврат пользовательского объекта из обернутого метода в Rcpp
У меня есть следующая проблема с модулем Rcpp:
допустим, что у меня есть два класса в модуле 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)
;
}
.
В настоящий момент компиляция завершается неудачно, поскольку она не знает, что делать с типом возврата A.
Я решил, что могу что-то сделать с Rcpp:: Xptr, но потом не могу подключить его к структуре S4, созданной Rcpp для класса A. Я фактически получаю внешний объект-указатель из метода в R.
Есть ли возможность вернуть корректно завернутый объект из метода второго класса?
Спасибо,
Томас
[править]
В соответствии с ответом Дирка я построил оболочку, которая может создать обернутый объект S4:
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 );
}
Тем не менее, я не знаю, как вернуть объект в качестве параметра метода/функции. Не работает следующее:
template <> A* as( SEXP obj){
Rcpp::List l(obj);
Rcpp::XPtr<A> xp( (SEXP) l[".pointer"] );
return (A*) xp;
}
Итак, как я могу получить внешний указатель на объект С++ из объекта S4, предоставленного в виде SEXP в списке параметров?
Ответы
Ответ 1
Эта функция была добавлена в Rcpp 0.10.0
Были и другие причины, по которым ваш код не компилировался. Следующий код работает для меня:
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)
;
}
Цена, которую нужно заплатить, - это вызвать макрос RCPP_EXPOSED_CLASS
для всех классов, который:
- Один из ваших модулей выставляет
- Вы хотите использовать как результат открытого метода или как параметр
С этим я получаю:
> b <- new( B )
> b$get_an_A( )
C++ object <0x10330a9d0> of class 'A' <0x1006f46e0>
> b$get_an_A( )$x
[1] 3
Ответ 2
Возможно, если вы напишите для него обертку. Те, кто не падает, как манна с неба, вам нужно помочь компилятору, предоставив его, и он будет затем выбран.
Простой пример из 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 ));
}
Здесь класс из Boost преобразуется в класс Rcpp (Date), который затем возвращается (и даже там нам нужен явный перенос). Для более сложных классов вам нужны более сложные преобразователи.
Edit: И, конечно же, вам нужно иметь в виду, что все, что может быть вызвано R, все равно должно соответствовать базовому интерфейсу SEXP foo(SEXP a, SEXP b, ...)
, предписанному .Call()
.
Ответ 3
Хорошо, я думаю, что понял. Но никаких гарантий.
Согласно ответу Дирка, я построил оболочку, которая может создать обернутый объект S4:
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 );
}
Если метод ожидает объект как параметр (здесь указатель на этот объект), может быть полезна следующая оболочка "как":
template <> A* as( SEXP obj){
Rcpp::Environment e(obj);
Rcpp::XPtr<A> xp( (SEXP) e.get(".pointer") );
return (A*) xp;
}
вы можете, конечно, также вернуть * xp, который позволяет принимать не указатели в качестве параметров, но это снова копирует объект.