Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error exposing c++ class method with reference parameter using Rcpp modules

Tags:

c++

r

rcpp

My goal is to build a dataset class and a model class, and expose both of them to R. The model class has a train() method that takes a reference to a dataset instance, and this seems to be at the root of my issue. Here's what this looks like

//glue.cpp

#include <Rcpp.h>

class MyData
{
public:
  MyData() = default;
};

class MyModel
{
public:
  MyModel() = default;
  void train(const MyData& data) { Rcpp::Rcout << "training model... "; };
};

// Expose MyData
RCPP_MODULE(MyData){
  Rcpp::class_<MyData>("MyData")
  .constructor()
  ;
}

// Expose MyModel
RCPP_MODULE(MyModel){
  Rcpp::class_<MyModel>("MyModel")
  .constructor()
  .method("train", &MyModel::train)
  ;
}

(I should note that this file, glue.cpp, is embedded in an R package.) When I remove this line, .method("train", &MyModel::train), I can compile this without error via pkgbuild::compile_dll(). With it, I get the nasty error below

─  installing *source* package ‘SimpleCppModel’ ...
   ** libs
   clang++  -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG  -I"/Library/Frameworks/R.framework/Versions/3.5/Resources/library/Rcpp/include" -I/usr/local/include  -std=c++14 -fPIC  -Wall -g -O2  -c RcppExports.cpp -o RcppExports.o
   clang++  -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG  -I"/Library/Frameworks/R.framework/Versions/3.5/Resources/library/Rcpp/include" -I/usr/local/include  -std=c++14 -fPIC  -Wall -g -O2  -c glue.cpp -o glue.o
   In file included from glue.cpp:3:
   In file included from /Library/Frameworks/R.framework/Versions/3.5/Resources/library/Rcpp/include/Rcpp.h:27:
   In file included from /Library/Frameworks/R.framework/Versions/3.5/Resources/library/Rcpp/include/RcppCommon.h:168:
   In file included from /Library/Frameworks/R.framework/Versions/3.5/Resources/library/Rcpp/include/Rcpp/as.h:25:
   /Library/Frameworks/R.framework/Versions/3.5/Resources/library/Rcpp/include/Rcpp/internal/Exporter.h:31:28: error: no matching constructor for initialization of 'MyData'
                       Exporter( SEXP x ) : t(x){}
                                            ^ ~
   /Library/Frameworks/R.framework/Versions/3.5/Resources/library/Rcpp/include/Rcpp/as.h:87:41: note: in instantiation of member function 'Rcpp::traits::Exporter<MyData>::Exporter' requested here
               ::Rcpp::traits::Exporter<T> exporter(x);
                                           ^
   /Library/Frameworks/R.framework/Versions/3.5/Resources/library/Rcpp/include/Rcpp/as.h:152:26: note: in instantiation of function template specialization 'Rcpp::internal::as<MyData>' requested here
           return internal::as<T>(x, typename traits::r_type_traits<T>::r_category());
                            ^
   /Library/Frameworks/R.framework/Versions/3.5/Resources/library/Rcpp/include/Rcpp/InputParameter.h:72:54: note: in instantiation of function template specialization 'Rcpp::as<MyData>' requested here
           ConstReferenceInputParameter(SEXP x_) : obj( as<T>(x_) ){}
                                                        ^
   /Library/Frameworks/R.framework/Versions/3.5/Resources/library/Rcpp/include/Rcpp/module/Module_generated_CppMethod.h:129:58: note: in instantiation of member function 'Rcpp::ConstReferenceInputParameter<MyData>::ConstReferenceInputParameter' requested here
           typename Rcpp::traits::input_parameter<U0>::type x0(args[0]);
                                                            ^
   /Library/Frameworks/R.framework/Versions/3.5/Resources/library/Rcpp/include/Rcpp/module/Module_generated_CppMethod.h:127:5: note: in instantiation of member function 'Rcpp::CppMethod1<MyModel, void, const MyData &>::operator()' requested here
       CppMethod1( Method m) : method_class(), met(m) {} 
       ^
   /Library/Frameworks/R.framework/Versions/3.5/Resources/library/Rcpp/include/Rcpp/module/Module_generated_method.h:59:27: note: in instantiation of member function 'Rcpp::CppMethod1<MyModel, void, const MyData &>::CppMethod1' requested here
       AddMethod( name_, new CppMethod1<Class,RESULT_TYPE,U0>(fun), valid, docstring);
                             ^
   glue.cpp:30:4: note: in instantiation of function template specialization 'Rcpp::class_<MyModel>::method<void, const MyData &>' requested here
     .method("train", &MyModel::train)
      ^
   glue.cpp:5:7: note: candidate constructor (the implicit copy constructor) not viable: cannot convert argument of incomplete type 'SEXP' (aka 'SEXPREC *') to 'const MyData' for 1st argument
   class MyData
         ^
   glue.cpp:5:7: note: candidate constructor (the implicit move constructor) not viable: cannot convert argument of incomplete type 'SEXP' (aka 'SEXPREC *') to 'MyData' for 1st argument
   glue.cpp:8:3: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
     MyData() = default;
     ^
   1 error generated.
   make: *** [glue.o] Error 1
   ERROR: compilation failed for package ‘SimpleCppModel’
─  removing ‘/private/var/folders/dn/9lp6j6j14t1137ftnnk27wyw0000gn/T/RtmpBqDLoF/devtools_install_4d775ed17ea2/SimpleCppModel’
Error in processx::run(bin, args = real_cmdargs, stdout_line_callback = real_callback(stdout),  : 
  System command error

What gives?

like image 676
Ben Avatar asked Jun 03 '26 19:06

Ben


1 Answers

As mentioned by Dirk in the comments, you will need as<>and wrap specializations for MyData. In your case, you can use the easiest solution from the "Extending Rcpp" vignette: RCPP_EXPOSED_CLASS. You just have to be careful when you include which header from Rcpp:

#include <Rcpp.h>

class MyData
{
public:
  MyData() = default;
};
RCPP_EXPOSED_CLASS(MyData)

class MyModel
{
public:
  MyModel() = default;
  void train(const MyData& data) { Rcpp::Rcout << "training model... " << std::endl; };
};

// Expose MyData
RCPP_MODULE(MyData){
  Rcpp::class_<MyData>("MyData")
  .constructor()
  ;
}

// Expose MyModel
RCPP_MODULE(MyModel){
  Rcpp::class_<MyModel>("MyModel")
  .constructor()
  .method("train", &MyModel::train)
  ;
}

/***R
myData <- new(MyData)
myModel <- new(MyModel)
myModel$train(myData) 
 */

As you found out in your answer, this even works after including Rcpp.h. It still makes sense to go through the mentioned gallery article as well the vignette, though.

like image 104
Ralf Stubner Avatar answered Jun 06 '26 08:06

Ralf Stubner