Сериализация Eigen :: Matrix с использованием библиотеки Cereal

ОБНОВЛЕНОМне удалось заставить его работать после того, как я погуглил и прочитал комментарии doxygen в коде. Проблема заключалась в том, что я пропустил актерский состав перед использованием resize() метод, а также не используя std::ios::binary для потоков. Если вы хотите сделать что-то подобное, лучше проверьте ответ Azoth.

Я пытаюсь сериализовать Eigen::Matrix типа с помощью зерновых. Это то, что у меня есть (слабо https://gist.github.com/mtao/5798888 и типы в cereal/types):

#include <cereal/cereal.hpp>
#include <cereal/archives/binary.hpp>
#include <Eigen/Dense>
#include <fstream>

namespace cereal
{
template <class Archive, class _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> inline
typename std::enable_if<traits::is_output_serializable<BinaryData<_Scalar>, Archive>::value, void>::type
save(Archive & ar, Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> const & m)
{
int rows = m.rows();
int cols = m.cols();
ar(make_size_tag(static_cast<size_type>(rows * cols)));
ar(rows);
ar(cols);
ar(binary_data(m.data(), rows * cols * sizeof(_Scalar)));
}

template <class Archive, class _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> inline
typename std::enable_if<traits::is_input_serializable<BinaryData<_Scalar>, Archive>::value, void>::type
load(Archive & ar, Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> const & m)
{
size_type size;
ar(make_size_tag(size));

int rows;
int cols;
ar(rows);
ar(cols);

const_cast<Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> &>(m).resize(rows, cols);

ar(binary_data(const_cast<_Scalar *>(m.data()), static_cast<std::size_t>(size * sizeof(_Scalar))));
}
}

int main() {
Eigen::MatrixXd test = Eigen::MatrixXd::Random(10, 3);
std::ofstream out = std::ofstream("eigen.cereal", std::ios::binary);
cereal::BinaryOutputArchive archive_o(out);
archive_o(test);

std::cout << "test:" << std::endl << test << std::endl;

out.close();

Eigen::MatrixXd test_loaded;
std::ifstream in = std::ifstream("eigen.cereal", std::ios::binary);
cereal::BinaryInputArchive archive_i(in);
archive_i(test_loaded);

std::cout << "test loaded:" << std::endl << test_loaded << std::endl;
}

3

Решение

Ваш код почти правильный, но содержит несколько ошибок:

Вам не нужно делать size_tag поскольку вы сериализуете количество строк и столбцов явно. Вообще использует зерновые size_tag для изменяемых размеров контейнеров, таких как векторы или списки. Несмотря на то, что размер матрицы можно изменить, имеет смысл просто явно сериализовать строки и столбцы.

  1. Ваша функция загрузки должна принимать свой параметр по неконстантной ссылке
  2. Вы не должны использовать оператор = с std::ofstream объекты
  3. Лучше, чтобы область видимости и RAII обрабатывали закрытие / снос std::ofstream а также зерновые архивы (бинарный архив немедленно удаляет свое содержимое, но в целом зерновые архивы гарантированно только сбрасывают свое содержимое при уничтожении)

Вот версия, которая компилирует и производит правильный вывод под g ++ и clang ++:

#include <cereal/cereal.hpp>
#include <cereal/archives/binary.hpp>
#include <Eigen/Dense>
#include <fstream>

namespace cereal
{
template <class Archive, class _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> inline
typename std::enable_if<traits::is_output_serializable<BinaryData<_Scalar>, Archive>::value, void>::type
save(Archive & ar, Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> const & m)
{
int32_t rows = m.rows();
int32_t cols = m.cols();
ar(rows);
ar(cols);
ar(binary_data(m.data(), rows * cols * sizeof(_Scalar)));
}

template <class Archive, class _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> inline
typename std::enable_if<traits::is_input_serializable<BinaryData<_Scalar>, Archive>::value, void>::type
load(Archive & ar, Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> & m)
{
int32_t rows;
int32_t cols;
ar(rows);
ar(cols);

m.resize(rows, cols);

ar(binary_data(m.data(), static_cast<std::size_t>(rows * cols * sizeof(_Scalar))));
}
}

int main() {
Eigen::MatrixXd test = Eigen::MatrixXd::Random(10, 3);

{
std::ofstream out("eigen.cereal", std::ios::binary);
cereal::BinaryOutputArchive archive_o(out);
archive_o(test);
}

std::cout << "test:" << std::endl << test << std::endl;

Eigen::MatrixXd test_loaded;

{
std::ifstream in("eigen.cereal", std::ios::binary);
cereal::BinaryInputArchive archive_i(in);
archive_i(test_loaded);
}

std::cout << "test loaded:" << std::endl << test_loaded << std::endl;
}
9

Другие решения

Основываясь на ответе @Azoth (в любом случае, я бы хотел отдать ему должное), я немного улучшил шаблон, чтобы

  • работать также для Eigen::Array (а не просто Eigen::Matrix);
  • не сериализовать измерения во время компиляции (что имеет довольно большое значение для хранения, например, Eigen::Vector3f).

Это результат:

namespace cereal
{
template <class Archive, class Derived> inline
typename std::enable_if<traits::is_output_serializable<BinaryData<typename Derived::Scalar>, Archive>::value, void>::type
save(Archive & ar, Eigen::PlainObjectBase<Derived> const & m){
typedef Eigen::PlainObjectBase<Derived> ArrT;
if(ArrT::RowsAtCompileTime==Eigen::Dynamic) ar(m.rows());
if(ArrT::ColsAtCompileTime==Eigen::Dynamic) ar(m.cols());
ar(binary_data(m.data(),m.size()*sizeof(typename Derived::Scalar)));
}

template <class Archive, class Derived> inline
typename std::enable_if<traits::is_input_serializable<BinaryData<typename Derived::Scalar>, Archive>::value, void>::type
load(Archive & ar, Eigen::PlainObjectBase<Derived> & m){
typedef Eigen::PlainObjectBase<Derived> ArrT;
Eigen::Index rows=ArrT::RowsAtCompileTime, cols=ArrT::ColsAtCompileTime;
if(rows==Eigen::Dynamic) ar(rows);
if(cols==Eigen::Dynamic) ar(cols);
m.resize(rows,cols);
ar(binary_data(m.data(),static_cast<std::size_t>(rows*cols*sizeof(typename Derived::Scalar))));
}
}
0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector