R: как сериализовать данные C / C ++, которые живут в куче? - PullRequest
3 голосов
/ 28 апреля 2019

Предположим, я хочу обернуть некоторый код C или C ++, который содержит массивы или векторы, которые не могут быть автоматически отображены на R-типы с помощью Rcpp, но которые мне нужно передать функциям 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, мне интересно, как я могу вернуть и обработать эти объекты внутри R / Rcpp таким образомспособ, которым вектор (или массив C, содержащий те же значения) может быть сериализован через saveRDS, а его значения восстановлены после readRDS.

Я думаю, один из способов сделать это будет через memcpy 'ingсодержимое объекта в некоторый вектор C ++ типа, который можно преобразовать в 'NumericVector' Rcpp или аналогичный, а затем принудительно привести его первый элемент к массиву требуемых типов C, когда его нужно использовать, но яинтересно, есть ли лучшее решение.

Ответы [ 2 ]

4 голосов
/ 29 апреля 2019

Если вы просто хотите сохранить данные 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), но вы должны быть осторожны, чтобы ваш класс имел только "простые старые данные""и ничего особенного, как указатели или обработчики файлов.

Более формальные варианты сериализации и примеры использования Rcpp можно увидеть здесь и здесь .

3 голосов
/ 29 апреля 2019

Я собирался предложить использовать библиотеку 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

Ссылки:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...