R:如何序列化堆中的C / C ++数据?

问题描述 投票:3回答:2

假设我想要包含一些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类型需要使用时,但我想知道是否有更好的解决方案。

r rcpp
2个回答
4
投票

如果您只想在以后的同一会话中保存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;
}

如果您仍然只想序列化,有很多选项,但您必须编写一些额外的自定义代码。

正如你所说,你可以将castmemcpy变成一个R向量(使用RawVector而不是NumericVector),但是你必须要小心你的类只有“普通旧数据”而且没有像指针或文件处理程序那样特殊。

使用Rcpp的更正式的序列化选项和示例可以看作herehere


3
投票

我打算建议将cereal库与Rcereal一起使用,但这似乎很难与union一起使用。如果一个人使用更多的C ++ - 比如boost::variant,那么它的效果非常好。我们的想法是序列化为对象为原始向量,然后可以使用saveRDSreadRDS保存和恢复。这里有一些示例代码:

#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

参考文献:

© www.soinside.com 2019 - 2024. All rights reserved.