Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

returning a custom object from a wrapped method in Rcpp

Tags:

c++

module

r

rcpp

I have the following problem with the Rcpp module: let's assume I've two classes in a Rcpp module

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)
  ;
}

.

Right now compilation fails as it does not know what to do with the return type of A.

I figured I could do something with Rcpp::Xptr, however, then I can't connect it to the S4 structure that Rcpp generated for the class A. I actually get an external pointer object from the method in R.

Is there any possiblity to get a correctly wrapped object back from a method of the second class?

Thanks, Thomas

[edit]

According to Dirk's answer I constructed a wrapper that can create the wrapped S4 object:

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 );
}

Still, I don't know how to get the object back in as an parameter to a method/function. The following is not working:

template <> A* as( SEXP obj){
  Rcpp::List l(obj);
  Rcpp::XPtr<A> xp( (SEXP) l[".pointer"] );
  return (A*) xp;
}

So how could I get the external pointer to the C++ object from the S4 object provided as SEXP in the parameter list?

like image 490
Thomas Handorf Avatar asked Sep 13 '12 11:09

Thomas Handorf


3 Answers

This feature has been added in Rcpp 0.10.0

There were other reasons why your code did not compile. The code below works for me:

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)
  ;
}

The price to pay is to call the macro RCPP_EXPOSED_CLASS for all classes that:

  • One of your module is exposing
  • You want to use as result of an exposed method or as a parameter

With this, I get:

> b <- new( B )
> b$get_an_A( )
C++ object <0x10330a9d0> of class 'A' <0x1006f46e0>
> b$get_an_A( )$x
[1] 3
like image 175
Romain Francois Avatar answered Oct 15 '22 08:10

Romain Francois


It is possible if you write a wrapper for it. Those don't fall like manna from the sky, you need to aid the compiler by providing it and it will then get picked.

Simple example from 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 ));
}

Here a class from Boost is converted to an Rcpp class (Date) which is then returned (and even there we need an explicit wrap). For more complex classes you need more complex converters.

Edit: And, of course, you need to keep in mind that anything callable from R still needs to conform to the basic SEXP foo(SEXP a, SEXP b, ...) interface prescribed by .Call().

like image 3
Dirk Eddelbuettel Avatar answered Oct 15 '22 09:10

Dirk Eddelbuettel


Ok, I think I got it. But no guaranties. According to Dirk's answer I constructed a wrapper that can create the wrapped S4 object:

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 );
}

If a method is expecting an object as parameter (here a pointer to that object), the following "as" wrapper may be useful:

template <> A* as( SEXP obj){
  Rcpp::Environment e(obj);
  Rcpp::XPtr<A> xp( (SEXP) e.get(".pointer") );
  return (A*) xp;
}

you may of course also return *xp which allows taking non-pointers as parameters , but that again copies the object.

like image 1
Thomas Handorf Avatar answered Oct 15 '22 09:10

Thomas Handorf