提升序列化:向前兼容失败,出现输入流错误

Boost serialization : forward compatibility fail with input stream error

本文关键字:失败 错误 输入流 序列化 升序      更新时间:2023-10-16

遵循此问题:提升序列化子类我正在尝试支持使用 boost 序列化生成的存档的向前兼容性,但我在使用旧代码读取较新的存档时遇到问题:

    class A {
    public:
        A() {}
        virtual ~A() = default;
    private:
        friend class boost::serialization::access;
        template <class Archive> void serialize(Archive &ar, const unsigned int version) {
            ar &mAttributeFromA;
        }
        std::string mAttributeFromA = "mAttributeFromA";
    };
    BOOST_CLASS_VERSION(A, 0)
    class B : public A {
    public:
        B() {}
    private:
        friend class boost::serialization::access;
        template <class Archive> void serialize(Archive &ar, const unsigned int version)
        {
            ar &boost::serialization::base_object<A>(*this);
            ar &mAttributeFromB;
            if (version == 1)
                ar &mNewAttribute;
        }
        std::string mAttributeFromB = "mAttributeFromB";
        std::string mNewAttribute = "mNewAttribute";
    };
    BOOST_CLASS_VERSION(B, 1)

    class Manager {
    public:
        boost::ptr_vector<A> mListOfA; // can store A or B
    private:
        friend class boost::serialization::access;
        template <class Archive> void serialize(Archive &ar, const unsigned int /*version*/) { ar &mListOfA; }
    };
    BOOST_CLASS_VERSION(Manager, 0)

    int main() {
        Manager  mgr;
        mgr.mListOfA.push_back(new B);
        mgr.mListOfA.push_back(new B);
        std::ofstream ofs("myFile.txt");
        {
            boost::archive::text_oarchive oa(ofs);
            oa << mgr;
        }
        try {
            Manager  mgr2;
            std::ifstream ifs("myFile.txt");
            boost::archive::text_iarchive ia(ifs);
            ia >> mgr2;
            mgr2.mListOfA.at(0);
        } catch(boost::archive::archive_exception e)
        {
            e.what();
        }
    }
BOOST_CLASS_EXPORT(A)
BOOST_CLASS_EXPORT(B)

这将生成以下存档:

22 serialization::archive 13 0 0 0 0 2 3 1 B 1 1
0 1 0
1 15 mAttributeFromA 15 mAttributeFromB 13 mNewAttribute 3
2
3 15 mAttributeFromA 15 mAttributeFromB 13 mNewAttribute

如果我尝试使用相同的代码重新加载存档,一切正常。

但是,如果我尝试使用旧版本的代码加载存档:(类版本为 0,mNewAttribute 消失了)

class B : public A {
    public:
        B() {}
    private:
        friend class boost::serialization::access;
        template <class Archive> void serialize(Archive &ar, const unsigned int version)
        {
            ar &boost::serialization::base_object<A>(*this);
            ar &mAttributeFromB;
        }
        std::string mAttributeFromB = "mAttributeFromB";
    };
    BOOST_CLASS_VERSION(B, 0)

反序列化给我一个"输入流错误"

在科里鲁

如何使用旧代码反序列化新存档?

--编辑--奇怪的是,如果我在管理器中添加 A B 对象,它就可以工作了。但是只有 A 或只有 B 失败...

您的类型不是多态的。版本控制可能与事情无关。

  • http://www.boost.org/doc/libs/1_60_0/libs/serialization/doc/serialization.html#derivedpointers

    事实证明,序列化的对象类型取决于基类(在本例中为 base)是否为多调类。如果 base 不是多态的,也就是说,如果它没有虚函数,则将序列化 base 类型的对象。任何派生类中的信息都将丢失。如果这是想要的(通常不是),那么不需要其他努力。

您可以轻松验证这一点:向量仅反序列化A s:

住在科里鲁

#include <boost/serialization/serialization.hpp>
#include <boost/serialization/version.hpp>
class A {
  public:
    A(){}
  private:
    friend class boost::serialization::access;
    template <class Archive> void serialize(Archive &ar, const unsigned int version) { ar &mAttributeFromA; }
    std::string mAttributeFromA = "mAttributeFromA";
};
BOOST_CLASS_VERSION(A, 0)
class B : public A {
  public:
    B(){}
  private:
    friend class boost::serialization::access;
    template <class Archive> void serialize(Archive &ar, const unsigned int version) {
        ar &mAttributeFromB;
        if (version == 1)
            ar &mNewAttribute;
    }
    std::string mAttributeFromB = "mAttributeFromB";
    std::string mNewAttribute   = "mNewAttribute";
};
BOOST_CLASS_VERSION(B, 1)
#include <boost/ptr_container/serialize_ptr_vector.hpp>
class Manager {
  public:
    boost::ptr_vector<A> mListOfA; // can store A or B
  private:
    friend class boost::serialization::access;
    template <class Archive> void serialize(Archive &ar, const unsigned int /*version*/) { ar &mListOfA; }
};
BOOST_CLASS_VERSION(Manager, 0)
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <sstream>
int main() {
    using namespace boost;
    std::stringstream ss;
    { 
        archive::text_oarchive oa(ss); 
        Manager mgr;
        mgr.mListOfA.push_back(new A);
        mgr.mListOfA.push_back(new B);
        oa << mgr;
    }
    std::cout << ss.str() << "n";
    { 
        archive::text_iarchive ia(ss); 
        Manager mgr;
        ia >> mgr;
        std::cout << "Deserialized: " << mgr.mListOfA.size() << "n";
    }
}

指纹

22 serialization::archive 13 0 0 0 0 2 2 1 0
0 15 mAttributeFromA 2
1 15 mAttributeFromA
Deserialized: 2

溶液:

  1. 使层次结构实际上具有多态性
  2. 添加基对象的序列化
  3. 注册派生类型
  4. ???
  5. 利润!

样品 (WIP) https://www.livecoding.tv/sehe/

住在科里鲁

#include <boost/serialization/serialization.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/version.hpp>
class A {
  public:
    A(){}
    virtual ~A() = default;
  private:
    friend class boost::serialization::access;
    template <class Archive> void serialize(Archive &ar, const unsigned int version) {
        ar &mAttributeFromA; 
    }
    std::string mAttributeFromA = "mAttributeFromA";
};
BOOST_CLASS_VERSION(A, 0)
class B : public A {
  public:
    B(){}
  private:
    friend class boost::serialization::access;
    template <class Archive> void serialize(Archive &ar, const unsigned int version)
    {
        ar &boost::serialization::base_object<A>(*this);
        ar &mAttributeFromB;
        if (version == 1)
            ar &mNewAttribute;
    }
    std::string mAttributeFromB = "mAttributeFromB";
    std::string mNewAttribute   = "mNewAttribute";
};
BOOST_CLASS_VERSION(B, 1)
BOOST_CLASS_EXPORT(A)
BOOST_CLASS_EXPORT(B)
#include <boost/ptr_container/serialize_ptr_vector.hpp>
class Manager {
  public:
    boost::ptr_vector<A> mListOfA; // can store A or B
  private:
    friend class boost::serialization::access;
    template <class Archive> void serialize(Archive &ar, const unsigned int /*version*/) { ar &mListOfA; }
};
BOOST_CLASS_VERSION(Manager, 0)
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <sstream>
int main() {
    using namespace boost;
    std::stringstream ss;
    { 
        archive::text_oarchive oa(ss); 
        Manager mgr;
        mgr.mListOfA.push_back(new A);
        mgr.mListOfA.push_back(new B);
        oa << mgr;
    }
    std::cout << ss.str() << "n";
    { 
        archive::text_iarchive ia(ss); 
        Manager mgr;
        ia >> mgr;
        std::cout << "Deserialized: " << mgr.mListOfA.size() << "n";
    }
}

指纹

22 serialization::archive 13 0 0 0 0 2 2 1 0
0 15 mAttributeFromA 3 1 B 1 1
1
2 15 mAttributeFromA 15 mAttributeFromB 13 mNewAttribute
Deserialized: 2

标准提升存档(包括二进制文件)不支持向前(向上)兼容性。

有一个 xml 存档的补丁(5 年前提出,仍然不包括在内),它允许部分向前兼容 - 跳过未知字段。

有一个实验性的3rdaprty ptree存档,它也支持添加新字段。

启用二进制存档的向前兼容性将需要对其进行重大修改。

Google 协议缓冲区提供开箱即用的向前兼容性。