Boost serialization : forward compatibility fail with input stream error - c++

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.

Related

Derived class serialization - 'unregistered class' exception

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

Is there a way to serialize iterators using boost serialization?

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()
};

Input stream error when partially deserializing derived class into base class using Boost

When I serialize a derived class using boost and try to deserialize only the base part, I get input stream error. I guess my code is wrong. Is there a way to deserialize only the base part of a derived object using boost archive?
Reason for this code is that I am trying to implement a design to send derived objects from one process to another. The receiving process will look at the ID in the base part to decide which derived object is received.
This is the test code with which I am trying to verify that this is possible using boost, but I get input stream error on executing this
class DataIface
{
public:
DataIface()
:num(0)
{
}
DataIface( int num):
num(num)
{
}
int num;
template< class Archive >
void serialize( Archive& ar, const unsigned int version )
{
std::cout<<"Serializing base class \n"<<std::endl;
ar & num;
}
};
class Data1 : public DataIface
{
private:
friend class boost::serialization::access;
public:
Data1()
:a(0)
{
};
Data1( int a, int num):
DataIface(num),
a(a)
{
}
int a;
template< class Archive >
void serialize( Archive& ar, const unsigned int version )
{
std::cout<<"Serializing derived class \n"<<std::endl;
ar & boost::serialization::base_object<DataIface>(*this);
ar & a;
}
};
int main()
{
Data1 obj(10, 20);
std::ostringstream oss;
boost::archive::text_oarchive oa( oss );
oa << obj;
Data1 obj2;
std::istringstream iss(oss.str());
boost::archive::text_iarchive ia( iss );
ia >> obj2;
cout<< obj2.a << std::endl;
cout << obj2.num << std::endl;
DataIface iface;
try
{
ia >> iface;
}
catch(std::exception& e)
{
std::cout<<e.what()<<std::endl;
}
cout << iface.num << std::endl;
return 0;
}
Any help would be appreciated
This is the test code that I am trying to verify that this is possible using boost and I get input stream error
What is the conclusion?
The conclusion is: it doesn't work. That's because it's not a feature. No where in the documentation does it even suggest you can do this.
Runtime Polymorphism
Just use polymorphism as intended!
Live On Coliru
#include <iostream>
#include <sstream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/export.hpp>
class DataIface {
public:
virtual ~DataIface() {}
template <class Archive> void serialize(Archive &ar, const unsigned int version) {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
class Data1 : public DataIface {
friend class boost::serialization::access;
public:
Data1(int a=0) : a(a) {}
int a;
template <class Archive> void serialize(Archive &ar, const unsigned int version) {
std::cout << __PRETTY_FUNCTION__ << "\n";
ar &boost::serialization::base_object<DataIface>(*this);
ar &a;
}
};
class Data2 : public DataIface {
friend class boost::serialization::access;
public:
Data2(int b=0) : b(b) {}
int b;
template <class Archive> void serialize(Archive &ar, const unsigned int version) {
std::cout << __PRETTY_FUNCTION__ << "\n";
ar &boost::serialization::base_object<DataIface>(*this);
ar &b;
}
};
BOOST_CLASS_EXPORT(Data1)
BOOST_CLASS_EXPORT(Data2)
int main() {
DataIface* tests[] = { new Data1(10), new Data2(-10) };
for(auto testobj : tests)
{
std::ostringstream oss;
{
boost::archive::text_oarchive oa(oss);
oa << testobj;
}
{
std::istringstream iss(oss.str());
boost::archive::text_iarchive ia(iss);
DataIface* obj = nullptr;
ia >> obj;
if (Data1* obj1 = dynamic_cast<Data1*>(obj))
std::cout << "It's a Data1: " << obj1->a << "\n";
if (Data2* obj2 = dynamic_cast<Data2*>(obj))
std::cout << "It's a Data2: " << obj2->b << "\n";
}
}
for(auto ptr : tests) delete ptr;
}
Prints:
void Data1::serialize(Archive&, unsigned int) [with Archive = boost::archive::text_oarchive]
void DataIface::serialize(Archive&, unsigned int) [with Archive = boost::archive::text_oarchive]
void Data1::serialize(Archive&, unsigned int) [with Archive = boost::archive::text_iarchive]
void DataIface::serialize(Archive&, unsigned int) [with Archive = boost::archive::text_iarchive]
It's a Data1: 10
void Data2::serialize(Archive&, unsigned int) [with Archive = boost::archive::text_oarchive]
void DataIface::serialize(Archive&, unsigned int) [with Archive = boost::archive::text_oarchive]
void Data2::serialize(Archive&, unsigned int) [with Archive = boost::archive::text_iarchive]
void DataIface::serialize(Archive&, unsigned int) [with Archive = boost::archive::text_iarchive]
It's a Data2: -10
Static Polymorphism
Alternatively, use a variant. This saves you the hassle of manual dynamic allocations and the potential cost of virtual dispatch.
Live On Coliru
#include <iostream>
#include <sstream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/variant.hpp>
class Data1 {
friend class boost::serialization::access;
public:
Data1(int a=0) : a(a) {}
int a;
template <class Archive> void serialize(Archive &ar, const unsigned int version) {
ar &a;
}
friend std::ostream& operator<<(std::ostream& os, Data1 const& d) {
return os << "It's a Data1: " << d.a;
}
};
class Data2 {
friend class boost::serialization::access;
public:
Data2(int b=0) : b(b) {}
int b;
template <class Archive> void serialize(Archive &ar, const unsigned int version) {
ar &b;
}
friend std::ostream& operator<<(std::ostream& os, Data2 const& d) {
return os << "It's a Data2: " << d.b;
}
};
int main() {
using V = boost::variant<Data1, Data2>;
V tests[] = { Data1{10}, Data2{-10} };
for(auto testobj : tests)
{
std::ostringstream oss;
{
boost::archive::text_oarchive oa(oss);
oa << testobj;
}
{
std::istringstream iss(oss.str());
boost::archive::text_iarchive ia(iss);
V deserialized;
ia >> deserialized;
std::cout << deserialized << "\n";
}
}
}
This prints:
It's a Data1: 10
It's a Data2: -10

Boost serialize child class

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

boost serialiaze input stream error

I am working on a simple serialization class. I keep throwing an exception on the input stream. I have put together the below example of what I am attempting to accomplish in simple terms.
I have this simple example of boost serialization that I am getting an exception on:
#include <boost/serialization/serialization.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
#define NVP(X) X
class base {
public:
friend class boost::serialization::access;
base (){ v1 = 10;}
int v1;
template<class Archive>
void serialize(Archive & ar, const unsigned int file_version)
{
ar & NVP(v1);
}
virtual void bla()=0;
};
class derived : public base {
public:
friend class boost::serialization::access;
int v2 ;
derived() { v2 = 100;}
template<class Archive>
void serialize(Archive & ar, const unsigned int file_version){
boost::serialization::base_object<base>(* this);
ar & NVP(v2);
}
virtual void bla(){};
};
BOOST_CLASS_EXPORT(base);
BOOST_CLASS_EXPORT_GUID(derived, "derived");
int main ( )
{
std::stringstream ss;
boost::archive::text_oarchive ar(ss);
base *b = new derived();
ar << NVP(b);
std::cout << ss.str()<<std::endl;
std::istringstream ssi;
base *b1 = new derived();
{
boost::archive::text_iarchive ar1(ssi);
ar1 >> b1;
}
//std::cout << ssi.str();
std::cout << "v1: " << b1->v1 << std::endl;
}
The exception that I am getting is:
terminate called after throwing an instance of 'boost::archive::archive_exception'
what(): input stream error
Any help would be appreciated.
You're reading from an empty stream:
std::istringstream ssi;
// ...
boost::archive::text_iarchive ar1(ssi);
Also, you leak this object:
base *b1 = new derived();
Here's a fixed example, notes:
it's very good practice/important to close archives before using the streamed data
BOOST_CLASS_EXPORT_GUID(derived, "derived") doesn't add anything beyond BOOST_CLASS_EXPORT(derived)
you can print the v2 conditionally:
if (auto* d = dynamic_cast<derived*>(b1))
std::cout << "v2: " << d->v2 << std::endl;
I've used bla() as an example to print the values instead
NVP() is a bit iffy there. Why not just leave it out for non-tagged archives (ie. other than XML)? If you intend to support XML, just use BOOST_SERIALIZATION_NVP, boost::serialization::make_nvp etc.
std::cout << "v2: " << b1->v2 << std::endl; was completely out of place
just initialize b1 to null so you don't leak it; remember to free all pointers (use smart pointers!)
the mix of public: and friend in your types didn't really mean much
Live On Coliru
#include <boost/serialization/serialization.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
#include <sstream>
class base {
public:
base(int v1) : v1(v1) {}
virtual void bla() const = 0;
private:
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &ar, unsigned /*int const file_version*/) {
ar & BOOST_SERIALIZATION_NVP(v1);
}
protected:
int v1;
};
class derived : public base {
public:
derived(int v1 = 10, int v2 = 100) : base(v1), v2(v2) {}
virtual void bla() const {
std::cout << "v1: " << v1 << ", v2: " << v2 << "\n";
}
private:
friend class boost::serialization::access;
int v2;
template <class Archive> void serialize(Archive &ar, unsigned /*int const file_version*/) {
boost::serialization::base_object<base>(*this);
ar & BOOST_SERIALIZATION_NVP(v2);
}
};
BOOST_CLASS_EXPORT(base)
BOOST_CLASS_EXPORT(derived)
int main() {
std::stringstream ss;
{
boost::archive::text_oarchive ar(ss);
base *b = new derived();
ar << boost::serialization::make_nvp("base", b);
delete b; // TODO use RAII instead
}
std::cout << ss.str() << std::endl;
base *deserialized = nullptr;
{
boost::archive::text_iarchive ar1(ss);
ar1 >> boost::serialization::make_nvp("base", deserialized);
}
deserialized->bla();
delete deserialized;
}
Prints
22 serialization::archive 12 0 7 derived 1 0
0 100
v1: 10, v2: 100