Ошибка выставления метода класса c ++ со ссылочным параметром с помощью модулей Rcpp - PullRequest
2 голосов
/ 17 марта 2019

Моя цель состоит в том, чтобы создать класс набора данных и класс модели, и представить оба из них в R. В классе модели есть метод train(), который принимает ссылку на экземпляр набора данных, и это, кажется, находится в корне.моей проблемы.Вот как это выглядит

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

(я должен отметить, что этот файл, glue.cpp, встроен в пакет R.) Когда я удаляю эту строку, .method("train", &MyModel::train), я могу скомпилировать ее без ошибокчерез pkgbuild::compile_dll().С этим я получаю неприятную ошибку ниже

─  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

Что дает?

Ответы [ 2 ]

3 голосов
/ 17 марта 2019

Как упоминал Дирк в комментариях, вам понадобятся as<> и wrap специализации для MyData. В вашем случае вы можете использовать самое простое решение из виньетки "Расширение Rcpp": RCPP_EXPOSED_CLASS. Вы просто должны быть осторожны, когда включаете заголовок из 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) 
 */

Как вы узнали в своем ответе, это даже работает после , включая Rcpp.h. Тем не менее, имеет смысл просмотреть упомянутую статью галереи, а также виньетка.

2 голосов
/ 18 марта 2019

Сначала я должен заявить, что пример в моем вопросе слишком прост. В действительности, класс MyData имеет метод, подобный void fill(const Rcpp::NumericMatrix& dat) { Rcpp::Rcout << "filling data... " << std::endl; };, он зависит от Rcpp::NumericMatrix, поэтому мне необходимо #include <Rcpp.h>, прежде чем я объявлю MyData.

Вот два решения, которые я нашел, но я не использовал их достаточно, чтобы узнать, есть ли какие-то "ошибки".

Решение 1 - макрос RCPP_EXPOSED_CLASS ()

Здесь я использую макрос RCPP_EXPOSED_CLASS () как , описанный Романом Франсуа . (Спасибо, Ральф, за то, что указал мне на этот самородок.)

#include <Rcpp.h>

class MyData;
class MyModel;

RCPP_EXPOSED_CLASS(MyData)
RCPP_EXPOSED_CLASS(MyModel)

class MyData
{
public:
  MyData() = default;
  void fill(const Rcpp::NumericMatrix& dat) { Rcpp::Rcout << "filling data... " << std::endl; };
};

  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()
  .method("fill", &MyData::fill)
  ;
}

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

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

Решение 2 - Используйте Rcpp :: XPtr

Это решение основано на шаблоне проектирования , обсужденном здесь . По сути, я просто создаю указатель на объект C ++ и использую функции-оболочки для его обработки. Вот пример того, как это может выглядеть.

#include <Rcpp.h>

class MyData
{
public:
  MyData() = default;
  void fill(const Rcpp::NumericMatrix& dat) { Rcpp::Rcout << "filling data... " << std::endl; };
};

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

// [[Rcpp::export]]
SEXP make_dataset() {
  MyData* datPtr = new MyData();
  Rcpp::XPtr<MyData> datXPtr(datPtr);
  return datXPtr;
}

// [[Rcpp::export]]
SEXP make_model() {
  MyModel* mdlPtr = new MyModel();
  Rcpp::XPtr<MyModel> mdlXPtr(mdlPtr);
  return mdlXPtr;
}

// [[Rcpp::export]]
void train_model(SEXP mdlXPtr, SEXP datXPtr) {
  Rcpp::XPtr<MyModel> mdlPtr(mdlXPtr);
  Rcpp::XPtr<MyData> datPtr(datXPtr);
  mdlPtr->train(*datPtr);
}

/***R
myData <- make_dataset()
myModel <- make_model()
train_model(myModel, myData)
*/

Недостатком этого метода является то, что, AFAIK, нет очевидного способа проверить тип объекта, на который указывает XPtr. Например, для меня не очевидно, как сделать недействительными плохие звонки, такие как train_model(myData, myModel).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...