How to serialize/deserialize derived class inheriting base class without default constructor?
Please offer serializing boost functions for the following classes
struct Base
{
Base(int b) : b(b) {}
const int b;
}
struct Derived : public Base
{
Derived(float d, int b) : Base(b), d(d) {}
const float d;
}
CodePudding user response:
The lack of a default constructor is the least of the problems here.
This class cannot be deserialized, as is. Once an instance of this class is constructed its const
class members will veto any further attempts to deserialize anything.
In this particular case your only option is to deserialize a lonely int
and a float
by themself. Then use the deserialized int
and float
to construct the class.
CodePudding user response:
Your use-case straddles two of the "special considerations" documented by Boost Serialization:
Note that I'm going to assume you want dynamic polymorphism, and to get this you need at least a virtual destructor. If you don't you will end up with Undefined Behaviour.
Combining the two for your example:
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/export.hpp>
#include <iostream>
struct Base {
Base(int b) : b(b) {}
virtual ~Base() = default;
const int b;
};
namespace boost::serialization {
template <typename Ar> inline void serialize(Ar&, Base&, unsigned) {}
template <typename Ar>
inline void save_construct_data(Ar& ar, Base const* p, unsigned) {
// save data required to construct instance
ar << p->b;
}
template <typename Ar>
inline void load_construct_data(Ar& ar, Base* p, unsigned) {
int attribute;
ar >> attribute;
// invoke inplace constructor to initialize instance
::new (p) Base(attribute);
}
} // namespace boost::serialization
struct Derived : public Base {
Derived(float d, int b) : Base(b), d(d) {}
const float d;
};
BOOST_CLASS_EXPORT(Base)
BOOST_CLASS_EXPORT(Derived)
namespace boost::serialization {
template <typename Ar> inline void serialize(Ar& ar, Derived& d, unsigned) {
ar & boost::serialization::base_object<Base>(d);
}
template <typename Ar>
inline void save_construct_data(Ar& ar, Derived const* p, unsigned) {
// save data required to construct instance
ar & p->b & p->d;
}
template <typename Ar>
inline void load_construct_data(Ar& ar, Derived* p, unsigned) {
int b;
float d;
ar & b & d;
// invoke inplace constructor to initialize instance
::new (p) Derived(d, b);
}
} // namespace boost::serialization
std::string save(Base* b) {
std::ostringstream oss;
{
boost::archive::text_oarchive oa(oss);
oa << b;
}
return oss.str();
}
Base* load(std::string txt) {
std::istringstream iss(std::move(txt));
boost::archive::text_iarchive ia(iss);
Base* b = nullptr;
ia >> b;
return b;
}
int main() {
for (Base* object :
{
new Base(-99),
static_cast<Base*>(new Derived(3.14, 42)),
}) //
{
std::cout << "----\n";
Base* roundtrip = load(save(object));
delete object;
std::cout << "roundtrip: b=" << roundtrip->b;
if (auto* as_derived = dynamic_cast<Derived const*>(roundtrip)) {
std::cout << ", d=" << as_derived->d;
}
std::cout << "\n";
delete roundtrip;
}
}
Prints
----
roundtrip: b=-99
----
roundtrip: b=42, d=3.14
SAFETY FIRST
Of course, don't use raw new/delete:
- using
unique_ptr
Live On Coliru - using
shared_ptr
(note thedynamic_pointer_cast
) Live On Coliru