Rcpp扩展自定义类时如何直接对操作自定义类的函数用Rcpp::export属性
解决方法
问题原因
Rcpp的导出属性生成机制默认无法识别自定义类的双向转换规则,哪怕你已经实现了SEXP构造函数和SEXP转换运算符,也需要显式给Rcpp注册转换规则,才能让它自动处理自定义类作为导出函数的参数和返回值。
实现步骤
你只需要在原有代码基础上补充少量内容即可,不需要修改原有逻辑:
- 在
#include<Rcpp.h>之前、TimeSerie类声明之后,给Rcpp的类型转换模板做特化声明,告诉Rcpp你的类支持和SEXP互转 - 在
#include<Rcpp.h>之后,实现这两个转换函数,直接复用你已经写好的构造、转换逻辑即可
完整可运行代码
#include<RcppCommon.h> class TimeSerie { protected: SEXP m_data; public: TimeSerie(SEXP); operator SEXP(); }; // 新增:给Rcpp注册转换规则声明 namespace Rcpp { template<> TimeSerie as(SEXP x); template<> SEXP wrap(const TimeSerie& x); } #include<Rcpp.h> using namespace Rcpp; // 原有构造函数和转换运算符完全不动 TimeSerie::TimeSerie(SEXP data): m_data(data) { if (! Rf_inherits(data,"ts")) stop("Not a ts object"); } TimeSerie::operator SEXP(){ return m_data; } // 新增:实现转换函数,直接复用已有逻辑 namespace Rcpp { template<> TimeSerie as(SEXP x) { return TimeSerie(x); } template<> SEXP wrap(const TimeSerie& x) { return (SEXP)x; } } // 直接导出使用TimeSerie的函数即可,无需写SEXP中转函数 // [[Rcpp::export]] TimeSerie ident_ts(TimeSerie x){ return x; }
测试效果
和你之前的中转函数效果完全一致:
Rcpp::sourceCpp("test.cpp") # 传入ts对象正常返回结果 ident_ts(ts(1:10)) # 传入非ts对象抛出预期错误 ident_ts("notcool")
简单说明
你可以把as和wrap理解成Rcpp和C++自定义类之间的固定翻译接口:
- 当R调用导出函数传入参数时,Rcpp会调用
as把R层的SEXP转成你需要的TimeSerie对象 - 当C++函数返回结果时,Rcpp会调用
wrap把TimeSerie对象转成R能识别的SEXP
你已经实现了类的构造、校验和转换逻辑,只需要把这两个逻辑对接给Rcpp的固定接口即可,不需要重复写代码。
内容的提问来源于stack exchange,提问作者Arnaud Feldmann
相关产品推荐
相关产品推荐

