I have base class User which is serializable :
class User
{
public:
User();
std::string GetLogin() const;
void SetLogin(std::string login);
protected:
std::string mLogin;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & mLogin;
}
};
This class can be inherited by other class like this :
class UserA : public User
{
UserA();
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & boost::serialization::base_object<User>(*this);
ar & mIsSomething;
}
bool mIsSomething = true;
}
To handle those user i have a "manager" class which contain a User vector :
class Manager
{
public:
bool Add(User user);
bool Remove(unsigned int index);
private:
std::vector<User> mUsers;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & mUsers;
}
};
So my manager can be filled with UserA , or UserB (never both at the same time). When i retrieve an element from Manager i simply cast it back to the correct child class.
This part is working fine.
But when i want to serialize the Manager class obviously Boost don't know which kind of User i'm trying to serialize and the extra fields from the child class are not serialized.
What are my solution here ?
Does my design is completly wrong ?
Should i specialize my manager class to something like this ?
class Manager
{
bool Add(UserA user);
bool Add(UserB user);
private:
std::vector<UserA> mUsersA;
std::vector<UserB> mUsersB;
}
So my manager can be filled with UserA , or UserB (never both at the same time)
No it can't:
std::vector<User> mUsers;
stores User objects by value. See What is object slicing?.
Thoughts
I'd also suggest templating the Manager on the concrete user type, but seeing how you use an actual type hierarchy, it seems you might be looking to actually use the runtime polymorphism.
Since serializing polymorphic types is somewhat more involved, let me show you a sample.
It also shows how to use e.g. boost::ptr_vector<> to manage the objects while storing them dynamically.
Live1 on Coliru
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/ptr_container/serialize_ptr_vector.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
class User
{
public:
User() {};
virtual ~User() {}
std::string GetLogin() const;
void SetLogin(std::string login);
protected:
std::string mLogin;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/)
{
ar & mLogin;
}
};
class UserA : public User
{
public:
UserA() {};
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/)
{
ar & boost::serialization::base_object<User>(*this);
ar & mIsSomething;
}
bool mIsSomething = true;
};
class UserB : public User
{
public:
UserB() {};
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/)
{
ar & boost::serialization::base_object<User>(*this);
ar & mIsSomethingElse;
}
bool mIsSomethingElse = true;
};
template <typename Tag>
class UserGen : public User
{
public:
UserGen() {};
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/)
{
ar & boost::serialization::base_object<User>(*this);
ar & mIsGen;
}
bool mIsGen = true;
};
struct GenA;
struct GenB;
struct GenC;
BOOST_CLASS_EXPORT(User)
BOOST_CLASS_EXPORT(UserA)
BOOST_CLASS_EXPORT(UserB)
BOOST_CLASS_EXPORT(UserGen<GenA>)
BOOST_CLASS_EXPORT(UserGen<GenB>)
BOOST_CLASS_EXPORT(UserGen<GenC>)
#include <boost/type_index.hpp>
class Manager
{
public:
template <typename User>
bool Add(User const& user) {
mUsers.push_back(new User(user));
return true; // FIXME?
}
bool Remove(unsigned int index) {
if (mUsers.size() > index) {
mUsers.erase(mUsers.begin()+index);
return true;
}
return false;
}
void dump() const {
for (auto& u : mUsers) {
std::cout << "user of type " << boost::typeindex::type_id_runtime(u) << "\n";
}
}
private:
boost::ptr_vector<User> mUsers;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/)
{
ar & mUsers;
}
};
#include <sstream>
#include <iostream>
int main() {
std::stringstream ss;
{
Manager man;
man.Add(UserA{});
man.Add(UserB{});
man.Add(UserGen<GenA>{});
man.Add(UserGen<GenB>{});
man.Add(UserGen<GenC>{});
boost::archive::text_oarchive oa(ss);
oa << man;
}
{
boost::archive::text_iarchive ia(ss);
Manager man;
ia >> man;
man.dump();
}
}
Prints
user of type UserA
user of type UserB
user of type UserGen<GenA>
user of type UserGen<GenB>
user of type UserGen<GenC>
1 linking boost 1.59 is somehow failing there :(
Thanks #m.s. for figuring out 1.58 still works
Related
Trying to get boost serialization to work. The main idea is to have a base class that contains a few members and some derived classes, each derived class has it's own private members alongside with the base class members.
This is a minimal version of what I'm trying to do:
class base {
public:
virtual void print() = 0;
private:
friend class boost::serialization::access;
template<class Archive> void serialize(Archive &ar, const unsigned) {}
}
class derived20 : public base {
private:
friend class boost::serialization::access;
template<class Archive> void serialize(Archive &ar, const unsigned) {
boost::serialization::base_object<base>(*this);
ar & var20;
}
int var20 = 20;
public:
void print() override { std::cout << var20 << std::endl; }
}
class derived40 : public base {
private:
friend class boost::serialization::access;
template<class Archive> void serialize(Archive &ar, const unsigned) {
boost::serialization::base_object<base>(*this);
ar & var40;
}
int var40 = 40;
public:
void print() override { std::cout << var40 << std::endl; }
}
BOOST_CLASS_EXPORT(derived20)
BOOST_CLASS_EXPORT(derived40)
BOOST_CLASS_EXPORT(base)
int main(int argc, char **argv) {
std::stringstream ss;
boost::archive::text_oarchive ar(ss);
auto d20 = new derived20();
ar << d20;
// prints:
// 22 serialization:archive 15 0 1 0
// 0 20
std::cout << ss.str() << std::endl;
boost::archive::text_iarchive iar(ss);
base *b;
iar >> b;
b->print(); // Should print "20"
}
I'm getting an archive_exception: unregistered class error.
What on earth am I doing wrong?
This is one rare occasion where auto might hurt:
auto d20 = new derived20();
ar << d20;
Contrast with:
base *b;
iar >> b;
Note how d20 is derived20*, not base*. So you're invoking Undefined Behaviour by attempting to deserialize a type from an archive that contains another type.
Simply changing that:
base* d20 = new derived20();
Fixes everything:
Also noticed that you forgot to actually serialize the base object.
Live on coliru
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/access.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <iostream>
class base {
public:
virtual void print() = 0;
private:
friend class boost::serialization::access;
template<class Archive> void serialize(Archive & /*unused*/, unsigned /*unused*/) {}
};
class derived20 : public base {
private:
friend class boost::serialization::access;
template<class Archive> void serialize(Archive &ar, unsigned /*unused*/) {
ar & boost::serialization::base_object<base>(*this)
& var20;
}
int var20 = 20;
public:
void print() override { std::cout << var20 << std::endl; }
};
class derived40 : public base {
private:
friend class boost::serialization::access;
template<class Archive> void serialize(Archive &ar, const unsigned /*unused*/) {
ar & boost::serialization::base_object<base>(*this)
& var40;
}
int var40 = 40;
public:
void print() override { std::cout << var40 << std::endl; }
};
BOOST_CLASS_EXPORT(derived20)
BOOST_CLASS_EXPORT(derived40)
BOOST_CLASS_EXPORT(base)
int main() {
std::stringstream ss;
{
boost::archive::text_oarchive ar(ss);
base* d20 = new derived20();
ar << d20;
}
// prints:
// 22 serialization:archive 15 0 1 0
// 0 20
std::cout << ss.str() << std::endl;
{
boost::archive::text_iarchive iar(ss);
base *b = nullptr;
iar >> b;
b->print(); // Should print "20"
}
}
Prints
22 serialization::archive 17 0 9 derived20 1 0
0 20
20
I'm using VS12 in my project and I'm defining a very simple class which serializes its attributes. However, when I put the BOOST_CLASS_EXPORT I obtain a syntax error:
syntax error : missing ';' before '<'
However, when I omit BOOST_CLASS_EXPORT, everything works fine. I was wondering if it could depend from the template specialization used by BOOST_CLASS_EXPORT (I've used BOOST_CLASS_EXPORT_GUID as well with the same error). Is there any bug in VS12 regarding this?
The snippet is:
namespace a { namespace b {
class BaseSerialization {
public:
BaseSerialization(const std::string& functionName) : functionName(functionName) { }
virtual void getFunctionName() const = 0;
private:
friend class ::boost::serialization::access;
template <class Archive> void serialize(Archive &ar, unsigned int const file_version) {
ar & BOOST_SERIALIZATION_NVP(functionName);
}
protected:
std::string functionName;
};
class FOOZZ : BaseSerialization {
public:
FOOZZ(const std::string& functionName) : BaseSerialization("a"), functionName(functionName) { }
virtual void getFunctionName() {
int a;
}
private:
friend class ::boost::serialization::access;
template <class Archive> void serialize(Archive &ar, unsigned /*int const file_version*/) {
ar & BOOST_SERIALIZATION_NVP(functionName);
}
protected:
std::string functionName;
};
BOOST_CLASS_EXPORT_GUID(FOOZZ, "FOOZZ")
// namespace a, namespace b
In my project I have a class containing and std::list and in another class I maintain an iterator pointing to a place in the middle of that list.
I can successfully serialize the list, but the iterator member variable is causing problems. Here is a program to reproduce:
#include <boost/serialization/list.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <iostream>
#include <fstream>
class A
{
public:
A(){}
A(const std::list<int> & _i) : i(_i) {}
virtual ~A(){}
std::list<int> i;
void display() {
std::cout << "i:";
for (const int j : i)
std::cout << " " << j;
std::cout << std::endl;
}
private:
friend class boost::serialization::access;
//friend std::ostream & operator<<(std::ostream &os, const A &a);
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & i;
}
};
class Stepper
{
public:
Stepper() {}
Stepper(const A& a)
: p(a.i.size()>0 ? a.i.begin() : a.i.end()) {}
std::list<int>::const_iterator p;
void display() {
std::cout << "p: " << *p << std::endl;
}
void step() { p++; }
private:
friend class boost::serialization::access;
//friend std::ostream & operator<<(std::ostream &os, const A &a);
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & p;
}
};
int main()
{
{
A a({5,6,7});
Stepper sa(a);
a.display();
sa.display();
sa.step();
sa.display();
std::ofstream ofs( "a.txt" );
boost::archive::text_oarchive ar(ofs);
ar & a;
ar & sa;
}
A b;
Stepper sb;
{
std::ifstream ifs( "a.txt" );
boost::archive::text_iarchive ar(ifs);
ar & b;
ar & sb;
}
b.display();
sb.display();
return 0;
}
In this program, the class A can be serialized without problems. (Remove the ar&sa stuff..) But unfortunately when trying to serialize the class containing the iterator (the exact code above), I get the following compilation errors:
[..snip..]
testser.cpp:72:10: required from here /usr/include/boost/serialization/access.hpp:116:11:
error: ‘struct std::_List_const_iterator<int>’ has no member named ‘serialize’
t.serialize(ar, file_version);
~~^~~~~~~~~
[..snip..]
testser.cpp:81:10: required from here /usr/include/boost/serialization/access.hpp:116:11:
error: ‘struct std::_List_const_iterator<int>’ has no member named ‘serialize’
So, it seems that boost/serialization/list.hpp does not support iterators. And yet, as far as I can tell, it's totally legitimate to keep an iterator to a list item somewhere, as they cannot be invalidated unless erased. Is there a way to serialize this iterator using boost? Do I need to write a custom function? Do I have to return a custom iterator from my std::list? (That sounds particularly ugly..)
Thanks for any insight.
Okay it seems the only way to do this is to split the serialization into save and load, and calculate the iterator's position in the list. This works as long as the iterator is valid. Unfortunately it means needing to add a pointer to the list to the structure, which I didn't want, but actually in my application I can access this so it is not a problem for me.
class Stepper
{
public:
Stepper() {}
Stepper(const A& _a)
: a(&_a), p(a->i.size()>0 ? a->i.begin() : a->i.end()) {}
const A* a;
std::list<int>::const_iterator p;
void display() {
std::cout << "p: " << *p << std::endl;
}
void step() { p++; }
private:
friend class boost::serialization::access;
template<class Archive>
void save(Archive &ar, const unsigned int version) const
{
int d = std::distance(a->i.begin(), p);
ar & a;
ar & d;
}
template<class Archive>
void load(Archive &ar, const unsigned int version)
{
int d;
ar & a;
ar & d;
p = a->i.begin();
for (; d>0; --d)
p++;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
The code below is my current thinking to permit boost::serialization of an immutable abstract base with virtual inheritance. I hope I missed something and there is a simpler solution...?
As it stands, it raises a few questions:
Is the comment in IObject::serialize valid?
The comment in bs::save_construct_data for House seems to indicate a bug in boost::serialization. Is that correct, or is there a better way to do this?
Is there a more elegant way to deserialise Building then the Deserialise function combined with a protected constructor?
The result of (3) is that another Building implementation will necessitate a chunk of duplicated code. I suspect this will require a bit of CRTP to mitigate - any alternatives?
How does one make this work if the virtual base contains data members? I suspect this is similar (or identical) to the Building::Deserialise.
#include <fstream>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/export.hpp>
namespace bs = boost::serialization;
// IBase comes from an external library, and we are not interested in
// serialising an IBase*.
class IBase {
public:
virtual ~IBase(){};
};
class IObject : public virtual IBase {
private:
friend class bs::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
std::cout << "Called IObject's serialize\n";
// IBase contains no members so there is no need to serialise it to/from the
// archive. However, the inheritance relationship must be registered in
// boost::serialization. We cannot use base_object to do this: It will try
// to static_cast *this to IBase, but we might not have created the instance
// yet, in which case there is no virtual table and a structured exception
// will be generated.
bs::void_cast_register<IObject, IBase>(static_cast<IObject *>(nullptr),
static_cast<IBase *>(nullptr));
}
public:
virtual ~IObject() {}
};
class IBuilding : public virtual IBase {
private:
friend class bs::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
std::cout << "Called IBuilding's serialize\n";
bs::void_cast_register<IBuilding, IBase>(static_cast<IBuilding *>(nullptr),
static_cast<IBase *>(nullptr));
}
public:
virtual ~IBuilding() {}
};
/* Tedious forward declarations to permit later friending. */
class Building;
class House;
namespace boost {
namespace serialization {
template <class Archive>
inline void save_construct_data(Archive &ar, const Building *t,
const unsigned int version);
template <class Archive>
inline void save_construct_data(Archive &ar, const House *t,
const unsigned int version);
template <class Archive>
inline void load_construct_data(Archive &ar, House *t,
const unsigned int version);
}
}
/* Tedious forward declarations end. */
class Building : public IBuilding, public IObject {
private:
friend class bs::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
std::cout << "Called Building's serialize\n";
// We can use base_object here because although the instance might not be
// created, the memory has been allocated. Since there is no virtual
// inheritance, the static_cast can succeed.
ar &bs::make_nvp("IObject", bs::base_object<IObject>(*this));
ar &bs::make_nvp("IBuilding", bs::base_object<IBuilding>(*this));
}
template <class Archive>
inline friend void bs::save_construct_data(Archive &ar, const Building *t,
const unsigned int version);
const double weight_;
protected:
const double height_;
// The Members, associated constructor, and Deserialise facilitate recreating
// this immutable base.
struct Members {
double weight_;
double height_;
};
Building(const Members &members)
: weight_(members.weight_), height_(members.height_) {}
template <class Archive> const Members Deserialise(Archive &ar) const {
double weight;
double height;
ar >> bs::make_nvp("weight_", weight) >> bs::make_nvp("height_", height);
return {weight, height};
}
public:
bool operator==(const Building &other) const {
return weight_ == other.weight_ && height_ == other.height_;
}
virtual double Height() const = 0;
};
class House : public Building {
private:
template <class Archive>
inline friend void bs::save_construct_data(Archive &ar, const House *t,
const unsigned int version);
template <class Archive>
inline friend void bs::load_construct_data(Archive &ar, House *t,
const unsigned int version);
template <class Archive>
explicit House(Archive &ar) : Building(Deserialise(ar)) {}
public:
House(double weight, double height) : Building({weight, height}) {}
virtual double Height() const { return height_; }
};
BOOST_CLASS_EXPORT(House);
namespace boost {
namespace serialization {
template <class Archive>
inline void save_construct_data(Archive &ar, const Building *t,
const unsigned int version) {
std::cout << "Called Building's save_construct_data\n";
ar << make_nvp("weight_", t->weight_) << make_nvp("height_", t->height_);
}
template <class Archive>
inline void bs::save_construct_data(Archive &ar, const House *t,
const unsigned int version) {
std::cout << "Called House's save_construct_data\n";
const auto &base = base_object<const Building>(*t);
ar << make_nvp("Building", base);
// ar << make_nvp("Building", &base); doesn't seem to work.
// Serialising out a reference calls Building's serialize method, the
// save_construct_data is only called for a pointer. This means we
// have to call it explicitly.
save_construct_data(ar, &base, version);
}
template <class Archive>
inline void bs::load_construct_data(Archive &ar, House *t,
const unsigned int version) {
std::cout << "Called House's load_construct_data\n";
ar >> make_nvp("Building", base_object<Building>(*t));
::new (t) House{ar};
}
}
}
int main() {
const char *file_name = "house.ser";
const bool save_first = true;
const House house(45367, 2.43);
std::cout << house.Height() << "\n";
const IObject *iHouse = &house;
if (save_first) {
std::ofstream ofs(file_name);
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_SERIALIZATION_NVP(iHouse);
}
IBuilding *iHouse2;
{
std::ifstream ifs(file_name);
boost::archive::xml_iarchive ia(ifs);
ia >> BOOST_SERIALIZATION_NVP(iHouse2);
}
if (dynamic_cast<const Building &>(*iHouse) ==
dynamic_cast<const Building &>(*iHouse2))
std::cout << "ok\n";
else
std::cout << "uh oh\n";
return 0;
}
I don't believe that the comment is correct. I believe that the void_cast_register is un-necessary since IBase is known to be a virtual base class of IObject.
Futhermore, if you don't add the serialization_support free functions for IBase, you won't be able to serialise/deserialise an IBase*, only an IObject* (although I think that's fine unless you are managing ownership through the IBase rather than the IObject).
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.
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 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";
}
}
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.