假设我想要包含一些C或C ++代码,这些代码包含无法通过Rcpp自动映射到R类型的数组或向量,但我需要传递给输出有效R对象的C / C ++函数。例如:
typedef union {
size_t val_sizet;
long double val_longdbl;
} weird_struct
std::vector<weird_struct> an_unmappable_obj;
an_unmappable_obj.resize(2);
an_unmappable_obj[0].val_sizet = 1e20;
an_unmappable_obj[1].val_longdbl = 1.5 * 1e20;
由于这是一个无法转换为R的本机类型的类型的向量,我想知道如何以这样的方式返回和处理R / Rcpp中的这些对象,使得向量(或包含相同值的C数组) )可以通过saveRDS
序列化,并在readRDS
之后恢复其值。
我想这样做的一种方法是通过memcpy
将对象的内容转换为某种类型的C ++向量,该向量可以转换为Rcpp的'NumericVector`或类似的,然后将其第一个元素强制转换为所需的数组C类型需要使用时,但我想知道是否有更好的解决方案。
如果您只想在以后的同一会话中保存C ++数据,最简单的方法是使用外部指针。例如。:
// [[Rcpp::export]]
Rcpp::XPtr< std::vector<double> > xptr_example() {
std::vector<double> * x = new std::vector<double>(10);
Rcpp::XPtr< std::vector<double> > p(x, true);
return p;
}
如果您仍然只想序列化,有很多选项,但您必须编写一些额外的自定义代码。
正如你所说,你可以将cast
和memcpy
变成一个R向量(使用RawVector
而不是NumericVector
),但是你必须要小心你的类只有“普通旧数据”而且没有像指针或文件处理程序那样特殊。
我打算建议将cereal
库与Rcereal一起使用,但这似乎很难与union
一起使用。如果一个人使用更多的C ++ - 比如boost::variant
,那么它的效果非常好。我们的想法是序列化为对象为原始向量,然后可以使用saveRDS
和readRDS
保存和恢复。这里有一些示例代码:
#include <Rcpp.h>
// [[Rcpp::plugins("cpp11")]]
// [[Rcpp::depends(BH, Rcereal)]]
#include <boost/variant.hpp>
#include <cereal/types/vector.hpp>
#include <cereal/types/boost_variant.hpp>
#include <cereal/archives/binary.hpp>
#include <sstream>
// [[Rcpp::export]]
Rcpp::RawVector get_weird_vec() {
std::vector<boost::variant<std::size_t, long double>> an_unmappable_obj;
an_unmappable_obj.push_back((std::size_t) 3e9);
an_unmappable_obj.push_back((long double) 1.5 * 1e20);
std::ostringstream os;
cereal::BinaryOutputArchive archive(os);
archive(an_unmappable_obj);
std::string out = os.str();
Rcpp::RawVector res(out.size());
std::copy(out.begin(), out.end(), res.begin());
return res;
}
// [[Rcpp::export]]
void process_weird_vec(Rcpp::RawVector src) {
std::stringstream ss;
ss.write(reinterpret_cast<char*>(&src[0]), src.size());
cereal::BinaryInputArchive archive(ss);
std::vector<boost::variant<std::size_t, long double>> an_unmappable_obj;
archive(an_unmappable_obj);
Rcpp::Rcout << an_unmappable_obj[0] << std::endl;
Rcpp::Rcout << an_unmappable_obj[1] << std::endl;
}
/*** R
raw <- get_weird_vec()
raw
process_weird_vec(raw)
*/
输出:
> raw <- get_weird_vec()
> raw
[1] 02 00 00 00 00 00 00 00 00 00 00 00 00 5e d0 b2 01 00 00 00 00 80 49 41 d4
[26] b0 1a 82 42 40 08 02
> process_weird_vec(raw)
3000000000
1.5e+20
参考文献: