Home > database >  Boost serialization base class without default constructor
Boost serialization base class without default constructor

Time:07-13

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:

Live On Coliru

#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:

  1. using unique_ptr Live On Coliru
  2. using shared_ptr (note the dynamic_pointer_cast) Live On Coliru
  • Related