使用谷歌库序列化Eigen :: Matrix

时间:2014-04-05 17:26:27

标签: c++ serialization c++11 eigen cereal

更新:我在谷歌搜索并阅读代码中的doxygen评论后设法让它工作。问题是我在使用resize()方法之前错过了演员表,也没有使用std::ios::binary来表示流。如果你想做类似的事情,最好检查一下Azoth的答案。

我正在尝试使用Cereal序列化Eigen::Matrix类型。这就是我所拥有的(松散地基于https://gist.github.com/mtao/5798888cereal/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;
}

2 个答案:

答案 0 :(得分:10)

您的代码几乎是正确的,但有一些错误:

由于您明确地序列化了行数和列数,因此您不需要创建size_tag。通常谷物使用size_tag用于可调整大小的容器,如矢量或列表。即使矩阵可以调整大小,仅仅明确地序列化行和列也更有意义。

  1. 您的加载函数应通过非const引用接受其参数
  2. 您不应该将operator =与std::ofstream个对象
  3. 一起使用
  4. 更好的做法是让范围界定和RAII处理std::ofstream以及谷物档案的关闭/拆除(二进制存档会立即清除其内容,但一般只保证谷物档案在破坏时冲洗他们的内容)
  5. 这是一个在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;
    }
    

答案 1 :(得分:0)

基于@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))));
    }
}