如何解决基于cereal序列化Eigen张量的编译错误?
Eigen张量的cereal序列化编译错误修正方案
问题背景
参考现有Eigen矩阵的cereal序列化实现,尝试编写Eigen张量的序列化代码,但编译时提示找不到有效的序列化方法。
原实现代码
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){ const auto& d = m.dimensions(); const int dims = d.size; for(int i=0;i<dims;i++) { ar(d[i]); } 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> const & m){ const auto& d = m.dimensions(); const int dims = d.size; for(int i=0;i<dims;i++) { ar(d[i]); } ar(binary_data(m.data(),static_cast<std::size_t>(m.size()*sizeof(typename Derived::Scalar)))); } }
测试代码
Eigen::Tensor<double, 3> tensor3dmaus = {4, 3, 2}; tensor3dmaus.setValues( {{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}} ); { std::ofstream out("eigen.cereal", std::ios::binary); cereal::BinaryOutputArchive archive_o(out); archive_o(tensor3dmaus); } std::cout << "test:" << std::endl << tensor3dmaus << std::endl; Eigen::Tensor<double,3> 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;
编译错误信息
Types must either have a serialize function, load/save pair, or load_minimal/save_minimal pair (you may not mix these). Serialize functions generally have the following signature: template<class Archive> void serialize(Archive & ar) { ar( member1, member2, member3 ); }
修正思路及代码
核心错误点
- load函数参数为const引用:加载需要修改张量内容与维度,const限制导致无法写入,cereal无法识别为有效load函数。
- 加载时未调整张量维度:原代码直接使用目标张量的初始维度,与保存的张量维度不匹配,会导致数据读写错误。
- 缺少显式的cereal特化声明:cereal需要明确知道该类型使用load/save对,而非默认的serialize函数。
修正后的序列化代码
namespace cereal { // 保存Eigen张量:兼容固定/动态维度 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, const Eigen::PlainObjectBase<Derived>& m) { const auto& dims = m.dimensions(); const std::size_t num_dims = dims.size(); // 先保存维度数量,提升通用性 ar(num_dims); // 保存各维度大小 for (std::size_t i = 0; i < num_dims; ++i) { ar(dims[i]); } // 二进制保存张量数据 ar(binary_data(m.data(), m.size() * sizeof(typename Derived::Scalar))); } // 加载Eigen张量:去掉const,支持修改张量 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) { std::size_t num_dims; ar(num_dims); // 读取各维度大小 std::vector<Eigen::Index> dims(num_dims); for (std::size_t i = 0; i < num_dims; ++i) { ar(dims[i]); } // 调整张量到目标维度 m.resize(dims); // 加载二进制数据 ar(binary_data(m.data(), m.size() * sizeof(typename Derived::Scalar))); } // 显式声明cereal使用load/save模式,解决编译报错 template <class Derived> struct specialization<Eigen::PlainObjectBase<Derived>, cereal::specialization::member_load_save> {}; }
测试代码注意事项
- 确保目标张量
test_loaded声明为Eigen::Tensor<double,3>(补充Eigen命名空间),加载时resize会自动调整其维度与保存的张量一致。
内容的提问来源于stack exchange,提问作者KeynesCoeFen
相关产品推荐
相关产品推荐

