liori
liori

Reputation: 42367

new_CppObject_xp incorrectly reports an error when passing an Rcpp object to another

I've got two Rcpp-wrapped classes, A and B. A's constructor extracts some information from its parameter, and prints it out to prove it worked.

B takes an object of A as a constructor parameter — and reports an error as if extraction in A's constructor failed. But as A did print out the extracted data, it couldn't fail.

How do I get B to accept A as a parameter without erroring out?

Minimal reproducible example:

library(Rcpp)

Rcpp::sourceCpp(code='
#include <Rcpp.h>

struct A {
  Rcpp::NumericVector y;

  A(Rcpp::List x)
    : y(Rcpp::as<Rcpp::NumericVector>(x["foo"]))
  {
    Rcpp::Rcout << y;
  }
};

struct B {
  B(A x) { }
};

RCPP_MODULE(A) {
  Rcpp::class_<A>("A")
  .constructor<Rcpp::List>();
}

RCPP_MODULE(B) {
  Rcpp::class_<B>("B")
  .constructor<A>();
}
')

Aobj <- new(A, list(foo=1:3))
Bobj <- new(B, Aobj)

Output:

> source('testcase.R', echo=TRUE)

> library(Rcpp)

> Rcpp::sourceCpp(code='
+ #include <Rcpp.h>
+ 
+ struct A {
+   Rcpp::NumericVector y;
+ 
+   A(Rcpp::List x)
+     : y(Rcpp::as<Rcpp::NumericVector> .... [TRUNCATED] 

> Aobj <- new(A, list(foo=1:3))
1 2 3
> Bobj <- new(B, Aobj)
Error in new_CppObject_xp(fields$.module, fields$.pointer, ...) : 
  Index out of bounds: [index='foo'].
> 

Upvotes: 3

Views: 275

Answers (2)

Ralf Stubner
Ralf Stubner

Reputation: 26833

I'll try to explain why your solution works. With RCPP_MODULE(A) you are exposing the C++ struct A as a reference class in R. That is done automatically. However, when you call Bobj <- new(B, Aobj), there is no information how to convert from this reference class to the required C++ struct. By using RCPP_EXPOSED_CLASS(A) you are creating specialization of Rcpp::wrap and Rcpp::as to convert between C++ and R objects in both ways. Since we are only missing the R to C++ conversion, i.e. Rcpp::as, the following is also enough:

#include <RcppCommon.h>
struct A;
RCPP_EXPOSED_AS(A)

#include <Rcpp.h>

struct A {
  Rcpp::NumericVector y;

  A(Rcpp::List x)
    : y(Rcpp::as<Rcpp::NumericVector>(x["foo"]))
  {
    Rcpp::Rcout << y;
  }
};

struct B {
  B(A x) { }
};

RCPP_MODULE(A) {
  Rcpp::class_<A>("A")
  .constructor<Rcpp::List>();
}

RCPP_MODULE(B) {
  Rcpp::class_<B>("B")
  .constructor<A>();
}

/*** R
Aobj <- new(A, list(foo=1:3))
Bobj <- new(B, Aobj)
*/

Upvotes: 4

liori
liori

Reputation: 42367

When things are not working, it's good to (re-)read some documentation. Specifically, Rcpp Extending (PDF), section 3.2.

I added the following at the beginning of the C++ code:

#include <RcppCommon.h>
struct A;
RCPP_EXPOSED_CLASS(A);

…and it works fine now. I admit I don't fully understand what happens here yet, but this solution is solving both the MWE and my original code problems.

Upvotes: 3

Related Questions