从Rcpp中的包装方法返回自定义对象

时间:2012-09-13 11:53:48

标签: c++ r module 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做一些事情然而,然后我无法将它连接到Rcpp为类A生成的S4结构。我实际上从R中的方法获得了一个外部指针对象。 / p>

是否有可能从第二类方法中获取正确包装的对象?

谢谢, 托马斯

[编辑]

根据Dirk的回答,我构建了一个可以创建包装的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;
}

那么如何从参数列表中作为SEXP提供的S4对象获取C ++对象的外部指针?

3 个答案:

答案 0 :(得分:11)

此功能已添加到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

答案 1 :(得分:3)

如果为它编写包装器,则可能是。那些不像天空中的吗哪,你需要通过提供它来帮助编译器,然后它将被选中。

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),然后返回(甚至在那里我们需要一个显式的换行)。对于更复杂的类,您需要更复杂的转换器。

编辑:当然,您需要记住,从R调用的任何内容仍然需要符合SEXP foo(SEXP a, SEXP b, ...)规定的基本.Call()接口。< / p>

答案 2 :(得分:1)

好的,我想我明白了。但没有保证。 根据Dirk的回答,我构建了一个可以创建包装的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 );
}

如果某个方法期望一个对象作为参数(这里是指向该对象的指针),则以下“as”包装器可能很有用:

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

你当然也可以返回* xp,它允许将非指针作为参数,但这又会复制对象。