【问题标题】:Boost serialization : forward compatibility fail with input stream errorBoost 序列化:前向兼容性失败,输入流错误
【发布时间】:2016-05-05 12:01:21
【问题描述】:

以下问题: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)

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

On Coliru

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

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

【问题讨论】:

  • 刚刚检查了链接的答案,它确实正确地描述了通过指向基址的指针序列化派生类型的要求。
  • 是的,您在另一个问题中的回答为我提供了能够序列化层次结构并支持向后兼容性的解决方案。我现在的问题是关于前向兼容性(旧代码的新存档)

标签: c++ serialization boost boost-serialization


【解决方案1】:

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

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

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. 利润!

样品(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-序列化,2-返回 BOOST_CLASS_VERSION(B, 0) 而不是 1 并尝试反序列化以前的存档。这就是让我崩溃的原因
  • 您真的尝试过我的代码吗?我没有检查,因为您在问题中说它在 ptr_vector 之外运行良好并且我可以看到我在答案中修复的 objective 错误。
  • 您的示例确实有效。我的真实类具有完全相同的骨架,但数据更复杂(地图、矢量等...)。至少您的示例排除了 ptr_vector 的问题。我打算做进一步的调查。谢谢
  • 不排除 ptr_vector 的问题。你可以看到它在那里。我问是因为 your 代码示例不完整/不完整。我不急于花时间看看为什么我的答案不起作用,如果我能看到的话。也许您可以创建自己的 SSCCE?
  • 我已经根据您的示例更新了我的问题。如果您运行一次(生成存档),然后将 B 类更改为版本 0(删除新属性),您将看到错误。
【解决方案2】:

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

有一个patch for xml archive(5 年前提出,目前还没有),它允许部分向前兼容——跳过未知字段。

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

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

Google protocol buffers 提供开箱即用的前向兼容性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多