You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 14:25:54