Boost 序列化:前向兼容性因输入流错误而失败
Boost serialization : forward compatibility fail with input stream error
关注这个问题:
我正在尝试支持通过 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
如果我尝试使用相同的代码重新加载存档,一切正常。
但是,如果我尝试使用旧版本的代码加载存档:(Class 版本为 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 and B 对象,它就会工作。但是只有 A 或 B 失败 ...
您的类型不是多态的。版本控制可能与事情无关。
http://www.boost.org/doc/libs/1_60_0/libs/serialization/doc/serialization.html#derivedpointers
It turns out that the kind of object serialized depends upon whether the base class (base in this case) is polymophic or not. If base is not polymorphic, that is if it has no virtual functions, then an object of the type base will be serialized. Information in any derived classes will be lost. If this is what is desired (it usually isn't) then no other effort is required.
你可以很容易地验证这一点:向量只反序列化 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
解决方案:
- 使层次结构实际多态
- 添加基础对象的序列化
- 注册派生类型
- ???
- 盈利!
样本(在制品)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
标准 boost 存档(包括二进制文件)不支持向前(向上)兼容性。
有一个 patch for xml archive(5 年前提出,仍未包含),它允许部分向前兼容 - 跳过未知字段。
有一个experimental 3rdaprty ptree archive,也支持添加新字段
启用二进制存档的向前兼容性将需要对其进行重大修改。
Google protocol buffers 提供开箱即用的向前兼容性。
关注这个问题:
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
如果我尝试使用相同的代码重新加载存档,一切正常。
但是,如果我尝试使用旧版本的代码加载存档:(Class 版本为 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 and B 对象,它就会工作。但是只有 A 或 B 失败 ...
您的类型不是多态的。版本控制可能与事情无关。
http://www.boost.org/doc/libs/1_60_0/libs/serialization/doc/serialization.html#derivedpointers
It turns out that the kind of object serialized depends upon whether the base class (base in this case) is polymophic or not. If base is not polymorphic, that is if it has no virtual functions, then an object of the type base will be serialized. Information in any derived classes will be lost. If this is what is desired (it usually isn't) then no other effort is required.
你可以很容易地验证这一点:向量只反序列化 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
解决方案:
- 使层次结构实际多态
- 添加基础对象的序列化
- 注册派生类型
- ???
- 盈利!
样本(在制品)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
标准 boost 存档(包括二进制文件)不支持向前(向上)兼容性。
有一个 patch for xml archive(5 年前提出,仍未包含),它允许部分向前兼容 - 跳过未知字段。
有一个experimental 3rdaprty ptree archive,也支持添加新字段
启用二进制存档的向前兼容性将需要对其进行重大修改。
Google protocol buffers 提供开箱即用的向前兼容性。