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 ...
Your types aren't polymorphic. Versioning likely has nothing to do with things.
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
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.