Boost 1.74与1.83序列化不兼容:multi_index容器归档无法读取
Boost多索引容器跨版本序列化兼容问题
我使用boost::multi_index_container存储一组带多属性索引的content对象,同时将一个params结构体序列化到二进制归档中,但遇到跨版本兼容问题:用Boost 1.74创建的归档,在Boost 1.83环境下读取时会抛出Invalid or corrupted archive错误,无法完成反序列化。
我准备了最小可复现示例(MRE),包含单个cpp文件、CMakeLists.txt和Dockerfile,可稳定复现该问题。
核心代码实现
content结构体
struct content{ friend class boost::serialization::access; using angle_type = std::size_t; inline content(angle_type angle): _angle(angle) {} inline angle_type angle() const { return _angle; } void reset_angle_random(){ static std::random_device dev; static std::mt19937 rng_angle(dev()); std::uniform_int_distribution<> angle_dist(0, 180); _angle = angle_dist(rng_angle); } void freeze(){ // complicated deterministic business logic _angle = 0; } content frozen() const{ mre::content copy(*this); copy.freeze(); return copy; } static content generate(){ static std::random_device dev; static std::mt19937 rng(dev()); std::uniform_real_distribution<> dist_length(-0.5f, 0.5f); mre::content content{0}; content._length = dist_length(rng); content.reset_angle_random(); return content; } template<class Archive> void serialize(Archive & ar, const unsigned int version) { ar & boost::serialization::make_nvp("length", _length); ar & boost::serialization::make_nvp("angle", _angle); } friend std::size_t hash_value(content const& c){ std::size_t seed = 0; boost::hash_combine(seed, c._length); boost::hash_combine(seed, c._angle); return seed; } inline std::size_t hash() const { return boost::hash<mre::content>{}(*this); } inline std::size_t frozen_id() const { return frozen().hash(); } inline std::string id() const { return (boost::format("%1%~%2%-%3%") % frozen_id() % hash() % angle()).str(); } inline bool operator<(const content& other) const { return id() < other.id(); } private: double _length; angle_type _angle; private: content() = default; };
multi_index容器配置(package结构体)
struct package{ friend class boost::serialization::access; struct tags{ struct id{}; struct content{}; struct angle{}; struct frozen{}; }; using container = boost::multi_index_container< mre::content, boost::multi_index::indexed_by< boost::multi_index::ordered_unique<boost::multi_index::identity<mre::content>>, boost::multi_index::ordered_unique<boost::multi_index::tag<tags::id>, boost::multi_index::const_mem_fun<mre::content, std::string, &mre::content::id>>, boost::multi_index::ordered_non_unique<boost::multi_index::tag<tags::content>, boost::multi_index::const_mem_fun<mre::content, std::size_t, &mre::content::hash>>, boost::multi_index::ordered_non_unique<boost::multi_index::tag<tags::angle>, boost::multi_index::const_mem_fun<mre::content, mre::content::angle_type, &mre::content::angle>>, boost::multi_index::ordered_non_unique<boost::multi_index::tag<tags::frozen>, boost::multi_index::const_mem_fun<mre::content, std::size_t, &mre::content::frozen_id>> > >; inline explicit package(const mre::parameters& params): _loaded(false), _parameters(params) {} inline explicit package(): _loaded(false) {} void save(const std::string& filename) const; void load(const std::string& filename); inline std::size_t size() const { return _samples.size(); } inline bool loaded() const { return _loaded; } const mre::content& operator[](const std::string& id) const; const mre::parameters& params() const { return _parameters; } template<class Archive> void serialize(Archive & ar, const unsigned int version) { ar & boost::serialization::make_nvp("samples", _samples); ar & boost::serialization::make_nvp("params", _parameters); } public: std::size_t generate(std::size_t contents, std::size_t angles); private: bool _loaded; container _samples; mre::parameters _parameters; };
parameters结构体
struct parameters{ std::size_t degree; std::size_t frame_size; template<class Archive> void serialize(Archive & ar, const unsigned int version) { ar & boost::serialization::make_nvp("degree", degree); ar & boost::serialization::make_nvp("frame_size", frame_size); } };
保存、加载和生成操作实现
void mre::package::save(const std::string& filename) const { std::ofstream stream(filename, std::ios::binary); try{ boost::archive::binary_oarchive out(stream, boost::archive::no_tracking); std::cout << "serialization library version: " << out.get_library_version() << std::endl; out << *this; } catch(const std::exception& e){ std::cout << "Error saving archive: " << e.what() << std::endl; } stream.close(); } void mre::package::load(const std::string& filename){ std::ifstream stream(filename, std::ios::binary); try{ boost::archive::binary_iarchive in(stream, boost::archive::no_tracking); std::cout << "serialization library version: " << in.get_library_version() << std::endl; in >> *this; _loaded = true; } catch(const std::exception& e){ std::cout << "Error loading archive: " << e.what() << std::endl; } stream.close(); } std::size_t mre::package::generate(std::size_t contents, std::size_t angles){ std::size_t count = 0; std::size_t v_content = 0; while(v_content++ < contents){ mre::content x = mre::content::generate(); std::size_t v_angle = 0; while(v_angle++ < angles){ mre::content x_angle = x; x_angle.reset_angle_random(); // commenting out this line makes it work if (_samples.insert(x_angle).second) ++count; } } return count; }
复现步骤
- 同版本Boost本地测试:
cd build cmake .. && make ./mre pack archive_name 10 # 序列化10个随机生成的content并保存到archive_name文件 ./mre unpack archive_name # 反序列化 - 跨版本测试:
make docker # 用Boost 1.74编译并在主机build/archives目录生成名为arc的归档文件 ./mre unpack archives/arc # 用本地Boost 1.83环境读取,会抛出异常
关键发现
注释掉generate函数中的x_angle.reset_angle_random();语句后,跨版本序列化/反序列化可以正常工作。怀疑是Boost multi_index容器的序列化实现存在跨版本兼容性问题,但未找到相关已知Bug。
内容的提问来源于stack exchange,提问作者Neel Basu
相关产品推荐
相关产品推荐

