提升序列化:前向兼容性因输入流错误而失败

时间:2016-01-28 08:09:23

标签: c++ serialization boost boost-serialization

关注此问题:Boost serialize child class 我尝试支持使用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)

反序列化给我一个&#34; 输入流错误&#34;

On Coliru

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

- 编辑 - 奇怪的是,如果我在经理内部添加A B对象,它就会正常工作。但只有A或只有B ...

2 个答案:

答案 0 :(得分:1)

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

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

<强> Live On Coliru

#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. 利润!
  6. 示例(WIP)https://www.livecoding.tv/sehe/

    <强> Live On Coliru

    #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
    

答案 1 :(得分:0)

标准增强档案(包括二进制)不支持向前(向上)兼容性。

patch for xml archive(5年前提出,仍未包括在内),它允许部分向前兼容 - 跳过未知字段。

有一个experimental 3rdaprty ptree archive,它还支持添加新字段。

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

Google protocol buffers提供开箱即用的远期兼容性。