【问题标题】:cereal + armadillo + json serialization谷物 + 犰狳 + json 序列化
【发布时间】:2016-11-14 13:41:49
【问题描述】:

有人有基于谷物的犰狳矩阵序列化为 JSON 的示例吗?下面的二进制序列化似乎正在工作。

mat_extra_meat.hpp 内

template<class Archive, class eT>
typename std::enable_if<cereal::traits::is_output_serializable<cereal::BinaryData<eT>, Archive>::value, void>::type
save( Archive & ar, const Mat<eT>& m ) {
    uword n_rows = m.n_rows;
    uword n_cols = m.n_cols;
    ar( n_rows );
    ar( n_cols );
    ar( cereal::binary_data(
        reinterpret_cast< void * const >( const_cast< eT* >( m.memptr() ) ),
        static_cast< std::size_t >( n_rows * n_cols * sizeof( eT ) ) ) );
}

template<class Archive, class eT>
typename std::enable_if<cereal::traits::is_input_serializable<cereal::BinaryData<eT>, Archive>::value, void>::type
load( Archive & ar, Mat<eT>& m ) {
    uword n_rows;
    uword n_cols;
    ar( n_rows );
    ar( n_cols );

    m.resize( n_rows, n_cols );

    ar( cereal::binary_data(
        reinterpret_cast< void * const >( const_cast< eT* >( m.memptr() ) ),
        static_cast< std::size_t >( n_rows * n_cols * sizeof( eT ) ) ) );
}

用这个测试:

int main( int argc, char** argv ) {

    arma::mat xx1 = arma::randn( 10, 20 );
    std::ofstream ofs( "test", std::ios::binary );
    cereal::BinaryOutputArchive o( ofs );
    o( xx1 );
    ofs.close();
    // Now load it.
    arma::mat xx2;
    std::ifstream ifs( "test", std::ios::binary );
    cereal::BinaryInputArchive i( ifs );
    i( xx2 );

}

【问题讨论】:

    标签: c++ armadillo cereal


    【解决方案1】:

    对于 JSON 序列化,您有两种选择 - 您可以采用一种不适合人类阅读的快速而肮脏的方法,或者您可以以增加序列化大小和时间为代价使其具有人类可读性。


    对于快速版本,您可以修改现有代码以使用 saveBinaryValueloadBinaryValue,它们存在于谷物的文本档案(JSON 和 XML)中。

    例如:

    ar.saveBinaryValue( reinterpret_cast<void * const>( const_cast< eT* >( m.memptr() ) ), 
                        static_cast<std::size_t>( n_rows * n_cols * sizeof( eT ) ) );
    

    对于负载也是如此。

    这将对您的数据进行 base64 编码并将其写入为字符串。您当然需要专门化该功能以仅适用于谷物中的文本档案(或仅适用于 JSON)。


    另一种方法是单独序列化每个元素。您在这里再次有两个选择,第一个是序列化为 JSON 数组(例如 myarray: [1, 2, 3, 4, 5, ...])或作为一堆单独的名称-值-对:“array1 " : "1", "array2": "2", ...

    谷物中的惯例是使用 JSON 数组来动态调整大小的容器(例如向量),但是因为我们在这个示例中纯粹是为了强调可读性,所以即使你的犰狳矩阵不是什么东西,我也会使用数组您希望用户能够使用 JSON 添加或删除元素:

    namespace arma
    {
      // Wraps a particular column in a class with its own serialization function.
      // This is necessary because cereal expects actual data to follow a size_tag, and can't
      // serialize two size_tags back to back without creating a new node (entering a new serialization function).
      //
      // This wrapper serves the purpose of creating a new node in the JSON serializer and allows us to
      // then serialize the size_tag, followed by the actual data
      template <class T>
      struct ColWrapper
      {
        ColWrapper(T && m, int c, int nc) : mat(std::forward<T>(m)), col(c), n_cols(nc) {}
        T & mat;
        int col;
        int n_cols;
    
        template <class Archive>
        void save( Archive & ar ) const
        {
          ar( cereal::make_size_tag( mat.n_rows ) );
          for( auto iter = mat.begin_col(col), end = mat.end_col(col); iter != end; ++iter )
            ar( *iter );
        }
    
        template <class Archive>
        void load( Archive & ar )
        {
          cereal::size_type n_rows;
    
          // Test to see if we need to resize the data
          ar( cereal::make_size_tag( n_rows ) );
          if( mat.n_rows != n_rows )
            mat.resize( n_rows, n_cols );
    
          for( auto iter = mat.begin_col(col), end = mat.end_col(col); iter != end; ++iter )
            ar( *iter );
        }
      };
    
      // Convenience function to make a ColWrapper
      template<class T> inline
      ColWrapper<T> make_col_wrapper(T && t, int c, int nc)
      {
        return {std::forward<T>(t), c, nc};
      }
    
      template<class Archive, class eT, cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value> = cereal::traits::sfinae>
      inline void save( Archive & ar, const Mat<eT>& m )
      {
        // armadillo stored in column major order
        uword n_rows = m.n_rows;
        uword n_cols = m.n_cols;
    
        // First serialize a size_tag for the number of columns. This will make expect a dynamic
        // sized container, which it will output as a JSON array. In reality our container is not dynamic,
        // but we're going for readability here.
        ar( cereal::make_size_tag( n_cols ) );
        for( auto i = 0; i < n_cols; ++i )
          // a size_tag must be followed up with actual serializations that create nodes within the JSON serializer
          // so we cannot immediately make a size_tag for the number of rows. See ColWrapper for more details
          ar( make_col_wrapper(m, i, n_cols) );
      }
    
      template<class Archive, class eT, cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value> = cereal::traits::sfinae>
      inline void load( Archive & ar, Mat<eT>& m )
      {
        // We're doing essentially the same thing here, but loading the sizes and performing the resize for the matrix
        // within ColWrapper
        cereal::size_type n_rows;
        cereal::size_type n_cols;
    
        ar( cereal::make_size_tag( n_cols ) );
        for( auto i = 0; i < n_cols; ++i )
          ar( make_col_wrapper(m, i, n_cols) );
      }
    } // end namespace arma
    

    运行上述示例程序:

    int main(int argc, char* argv[])
    {
      std::stringstream ss;
      std::stringstream ss2;
    
      {
        arma::mat A = arma::randu<arma::mat>(4, 5);
        cereal::JSONOutputArchive ar(ss);
        ar( A );
      }
    
      std::cout << ss.str() << std::endl;
    
      {
        arma::mat A;
        cereal::JSONInputArchive ar(ss);
        ar( A );
        {
          cereal::JSONOutputArchive ar2(ss2);
          ar2( A );
        }
      }
    
      std::cout << ss2.str() << std::endl;
    
      return 0;
    }
    

    及其输出:

    {
        "value0": [
            [
                0.786820954867802,
                0.2504803406880287,
                0.7106712289786555,
                0.9466678009609704
            ],
            [
                0.019271058195813773,
                0.40490214481616768,
                0.25131781792803756,
                0.02271243862792676
            ],
            [
                0.5206431525734917,
                0.34467030607918777,
                0.27419560360286257,
                0.561032100176393
            ],
            [
                0.14003945653337478,
                0.5438560675050177,
                0.5219157100717673,
                0.8570772835528213
            ],
            [
                0.49977436000503835,
                0.4193700240544483,
                0.7442805199715539,
                0.24916812957858262
            ]
        ]
    }
    {
        "value0": [
            [
                0.786820954867802,
                0.2504803406880287,
                0.7106712289786555,
                0.9466678009609704
            ],
            [
                0.019271058195813773,
                0.40490214481616768,
                0.25131781792803756,
                0.02271243862792676
            ],
            [
                0.5206431525734917,
                0.34467030607918777,
                0.27419560360286257,
                0.561032100176393
            ],
            [
                0.14003945653337478,
                0.5438560675050177,
                0.5219157100717673,
                0.8570772835528213
            ],
            [
                0.49977436000503835,
                0.4193700240544483,
                0.7442805199715539,
                0.24916812957858262
            ]
        ]
    }
    

    【讨论】:

    • 感谢 Azoth。 json serailization 似乎仍然存在一些问题。 JSON看起来如下(列表的关闭支架){“value0”:[1.0198155217002611,-1.7958489999259313,[0.4132334887432163,-0.41711609563173047 [0.8838092741653173047],[1.0883809274165732,-1.0135035381568509,[] span>
    • 你知道为什么 ar( grain::make_size_tag( n_rows ) );不会导致 json 给 json 数组添加右括号?
    • 我会尝试安装犰狳,看看出了什么问题。
    • 我已经用一个实际可行的解决方案更新了我的答案。我对发生的事情的解释有点欠缺,但如果你深入研究 JSON 序列化程序并阅读我的 cmets,它应该是有意义的。
    • 谢谢Azoth!有效。现在我正在尝试将您的解决方案扩展到 arma::cube,但不幸的是没有取得多大成功
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-24
    • 1970-01-01
    • 2016-01-26
    • 1970-01-01
    • 2012-04-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多