How to serialize an object with shared_ptr using boost
There are abstract I1
and derivatives C1
.
There are abstract I2
and derivatives C2
.
I1
have shared_ptr<I2>
. How can I make them serializable using boost serializaton? I do this, but my application is getting an exception.
#include <sstream>
#include <boost/shared_ptr.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
struct I1
{
I1() {}
virtual ~I1() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
}
};
struct C1 : I1
{
virtual ~C1() {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I1>(*this);
}
};
struct I2
{
virtual ~I2() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & p;
}
boost::shared_ptr<I1> p;
};
struct C2 : I2
{
C2() { p = boost::shared_ptr<I1>(new C1); }
virtual ~C2() { }
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I2>(*this);
}
};
int main()
{
C2 c2;
std::string s;
std::stringstream ss(s);
boost::archive::binary_oarchive oa(ss);
oa.register_type<I1>();
oa.register_type<C1>();
oa.register_type<I2>();
oa.register_type<C2>();
oa << c2;
boost::archive::binary_iarchive ia(ss);
//ia.register_type<I1>(); // cannot instantiate abstract class
ia.register_type<C1>();
//ia.register_type<I2>(); // cannot instantiate abstract class
ia.register_type<C2>();
ia >> c2;
}
source to share
The batch serialization documentation says interesting things here about BOOST_CLASS_EXPORT
:
... BOOST_CLASS_EXPORT ...
Therefore, the need for an export is implied by the use of a derived class, which is controlled by a pointer or reference to its base class.
The pointer p
does exactly that. Adding these macros to your code also gets rid of the ugly explicit calls register_type()
from your main one, which is nice too :)
So this code seems to compile and work in VS2014:
#include <sstream>
#include <boost/shared_ptr.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/export.hpp>
struct I1
{
I1() {}
virtual ~I1() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
}
};
BOOST_CLASS_EXPORT(I1)
struct C1 : I1
{
virtual ~C1() {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I1>(*this);
}
};
BOOST_CLASS_EXPORT(C1)
struct I2
{
virtual ~I2() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & p;
}
boost::shared_ptr<I1> p;
};
BOOST_CLASS_EXPORT(I2)
struct C2 : I2
{
C2() { p = boost::shared_ptr<I1>(new C1); }
virtual ~C2() { }
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I2>(*this);
}
};
BOOST_CLASS_EXPORT(C2)
int main()
{
C2 c2;
std::string s;
std::stringstream ss(s);
boost::archive::binary_oarchive oa(ss);
oa << c2;
boost::archive::binary_iarchive ia(ss);
ia >> c2;
}
Interestingly, however, the expression from the Boost docs is clearly wrong for all compilers, and a lot of code examples on the internet just don't work in VS2014.
source to share
Add to
BOOST_SERIALIZATION_ASSUME_ABSTRACT(I1) BOOST_SERIALIZATION_ASSUME_ABSTRACT(I2)
As per the documentation http://www.boost.org/doc/libs/1_39_0/libs/serialization/doc/traits.html#abstract
UPDATE
I just checked with VS2013RTM and Boost 1_55, this is JustWorks (TM), I
- removed type registration for abstract bases (they can never be fully loaded from the archive)
-
added
#pragma warning(disable: 4244) #include <boost/config/warning_disable.hpp>
at the top of the file to disable known chat alerts
Compiled code and works without errors. For good style, you should probably
- serialize c2 through a pointer (stack variables can subtly un-track objects )
Here's the complete code I ended up with:
#pragma warning(disable: 4244)
#include <boost/config/warning_disable.hpp>
#include <sstream>
#include <boost/shared_ptr.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
struct I1
{
I1() {}
virtual ~I1() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
}
};
struct C1 : I1
{
virtual ~C1() {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I1>(*this);
}
};
struct I2
{
virtual ~I2() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & p;
}
boost::shared_ptr<I1> p;
};
struct C2 : I2
{
C2() { p = boost::shared_ptr<I1>(new C1); }
virtual ~C2() { }
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I2>(*this);
}
};
int main()
{
boost::shared_ptr<I2> c2(new C2);
std::string s;
std::stringstream ss(s);
boost::archive::text_oarchive oa(ss);
oa.register_type<C1>();
oa.register_type<C2>();
oa << c2;
std::cout << "Serialized form: '" << ss.str() << "'\n";
boost::archive::text_iarchive ia(ss);
ia.register_type<C1>();
ia.register_type<C2>();
ia >> c2;
}
And here's the output:
source to share
I think you should also not register your pure virtual classes in the output archive.
Alternatively, you can use export . After defining your classes, replace the rest of your code with the following:
#include <boost/serialization/export.hpp>
BOOST_CLASS_EXPORT(C1)
BOOST_CLASS_EXPORT(C2)
int main()
{
C2 c2;
std::string s;
std::stringstream ss(s);
{
boost::archive::binary_oarchive oa(ss);
oa << c2;
}
boost::archive::binary_iarchive ia(ss);
ia >> c2;
}
I put the output archive in a separate block. I think it most likely works without it, but I want to make sure everything turns red (via out of scope).
source to share