c++ - boost::serialization with immutable abstract base and virtual inheritance -
the code below current thinking permit boost::serialization
of immutable abstract base virtual inheritance. hope missed , there simpler solution...?
as stands, raises few questions:
- is comment in
iobject::serialize
valid? - the comment in
bs::save_construct_data
house
seems indicate bug inboost::serialization
. correct, or there better way this? - is there more elegant way deserialise
building
deserialise
function combined protected constructor? - the result of (3)
building
implementation necessitate chunk of duplicated code. suspect require bit of crtp mitigate - alternatives? - how 1 make work if virtual base contains data members? suspect similar (or identical)
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 external library, , not interested in // serialising 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 there no need serialise to/from // archive. however, inheritance relationship must registered in // boost::serialization. cannot use base_object this: try // static_cast *this ibase, might not have created instance // yet, in case there no virtual table , structured exception // 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 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"; // can use base_object here because although instance might not // created, memory has been allocated. since there no virtual // inheritance, 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_; // members, associated constructor, , deserialise facilitate recreating // 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 work. // serialising out reference calls building's serialize method, // save_construct_data called pointer. means // have call 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 comment correct. believe void_cast_register
un-necessary since ibase
known virtual base class of iobject
.
futhermore, if don't add serialization_support free functions ibase
, won't able serialise/deserialise ibase*
, iobject*
(although think that's fine unless managing ownership through ibase
rather iobject
).
Comments
Post a Comment