问题
Following this question : Boost serialize child class I'm trying to support forward compatibility for my archive generated with boost serialization but i'm having trouble reading a newer archive with older code :
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)
this will generate the following archive :
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
If i try to reload the archive with the same code , everything works perfectly.
However if i try to load the archive with an older version of the code : (Class version is 0 and mNewAttribute is gone)
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)
The deserialization throw me an "input stream error"
On Coliru
How can i deserialize new archive with old code ?
-- Edit -- Strangely, if i add A and B objects inside the manager it's working. But fail with only A or only B ...
回答1:
Your types aren't polymorphic. Versioning likely has nothing to do with things.
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.
You can verify this easily: the vector only deserializes 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";
}
}
Prints
22 serialization::archive 13 0 0 0 0 2 2 1 0
0 15 mAttributeFromA 2
1 15 mAttributeFromA
Deserialized: 2
Solution:
- Make the hierarchy actually polymorphic
- Add serialization of base object
- Register derived types
- ???
- Profit!
Sample (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";
}
}
Prints
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
回答2:
Standard boost archives (including binary) do not support forward (upward) compatibility.
There is a patch for xml archive (proposed 5 years ago, still not included), which allows partial forward compatibility - skipping unknown fields.
There is an experimental 3rdaprty ptree archive, which also supports adding new fields.
Enabling forward compatibility for binary archive will require significant modifications in it.
Google protocol buffers offer forward compatibility out of the box.
来源:https://stackoverflow.com/questions/35055936/boost-serialization-forward-compatibility-fail-with-input-stream-error