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

R: how to serialize C/C++ data that lives in the heap?

假设我想包装一些 C 或 C++ 代码,其中包含无法通过 Rcpp 自动映射到 R 类型的数组或向量,但我需要将其传递给 C/C++ 输出有效的函数R对象。例如:

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 的原生类型,我想知道如何 return 并以这种方式处理 R/Rcpp 中的这些对象向量(或包含相同值的 C 数组)可以通过 saveRDS 序列化并在 readRDS.

之后恢复其值

我想这样做的一种方法是通过 memcpy 将对象的内容转换为某种可以转换为 Rcpp 的“NumericVector”或类似类型的 C++ 向量,然后强制转换当需要使用它时,它的第一个元素到所需的 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;
}

如果您仍然只想序列化,则有很多选择,但您将不得不编写一些额外的自定义代码。

如您所说,您可以将 castmemcpy 放入 R 向量中(使用 RawVector 而不是 NumericVector),但您必须小心class 只有 "plain old data" 并且没有像指针或文件处理程序这样的特殊内容。

可以看到更多正式的序列化选项和使用 Rcpp 的示例 here and here

我打算建议将 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

参考文献: