使用 Rcpp 模块公开带有引用参数的 c++ class 方法时出错
Error exposing c++ class method with reference parameter using Rcpp modules
我的目标是构建数据集 class 和模型 class,并将它们都暴露给 R。模型 class 有一个 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)
时,我可以通过 [=15= 编译它而不会出错].有了它,我得到了下面令人讨厌的错误
─ 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
什么给了?
正如德克在评论中提到的,您将需要 as<>
和 wrap
专业化 MyData
。对于您的情况,您可以使用 "Extending 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
。
不过,浏览上述画廊文章和小插图仍然有意义。
首先我要声明我问题中的例子过于简单。实际上,MyData class 有一个类似于 void fill(const Rcpp::NumericMatrix& dat) { Rcpp::Rcout << "filling data... " << std::endl; };
的方法,它依赖于 Rcpp::NumericMatrix
,所以我 在声明之前必须 #include <Rcpp.h>
我的数据。
这是我找到的两个解决方案,但我没有充分使用它们,不知道是否有"gotchas"。
解决方案 1 - RCPP_EXPOSED_CLASS() 宏
这里我使用RCPP_EXPOSED_CLASS()宏作为described by Romain Francois。 (感谢 Ralf,向我指出了这个要点。)
#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)
.
之类的错误调用无效对我来说并不明显
我的目标是构建数据集 class 和模型 class,并将它们都暴露给 R。模型 class 有一个 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)
时,我可以通过 [=15= 编译它而不会出错].有了它,我得到了下面令人讨厌的错误
─ 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
什么给了?
正如德克在评论中提到的,您将需要 as<>
和 wrap
专业化 MyData
。对于您的情况,您可以使用 "Extending 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
。
不过,浏览上述画廊文章和小插图仍然有意义。
首先我要声明我问题中的例子过于简单。实际上,MyData class 有一个类似于 void fill(const Rcpp::NumericMatrix& dat) { Rcpp::Rcout << "filling data... " << std::endl; };
的方法,它依赖于 Rcpp::NumericMatrix
,所以我 在声明之前必须 #include <Rcpp.h>
我的数据。
这是我找到的两个解决方案,但我没有充分使用它们,不知道是否有"gotchas"。
解决方案 1 - RCPP_EXPOSED_CLASS() 宏
这里我使用RCPP_EXPOSED_CLASS()宏作为described by Romain Francois。 (感谢 Ralf,向我指出了这个要点。)
#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
此解决方案基于
#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)
.