下面的代码是我目前的想法,即允许boost::serialization
具有虚拟继承的不可变抽象基础。我希望我错过了一些更简单的解决方案......?
目前,它提出了几个问题:
IObject::serialize
中的评论是否有效?bs::save_construct_data
House
中的评论似乎表明boost::serialization
中存在错误。这是正确的,还是有更好的方法来做到这一点?Building
,然后将Deserialise
函数与受保护的构造函数结合使用?Building
实现将需要一大堆重复代码。我怀疑这需要一些CRTP来缓解 - 任何替代方案?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 from an external library, and we are not interested in
// serialising an 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 so there is no need to serialise it to/from the
// archive. However, the inheritance relationship must be registered in
// boost::serialization. We cannot use base_object to do this: It will try
// to static_cast *this to IBase, but we might not have created the instance
// yet, in which case there is no virtual table and a structured exception
// will be 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 to 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";
// We can use base_object here because although the instance might not be
// created, the memory has been allocated. Since there is no virtual
// inheritance, the 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_;
// The Members, associated constructor, and Deserialise facilitate recreating
// this 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 to work.
// Serialising out a reference calls Building's serialize method, the
// save_construct_data is only called for a pointer. This means we
// have to call it 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;
}
答案 0 :(得分:0)
我不相信评论是正确的。我认为void_cast_register
是不必要的,因为已知IBase
是IObject
的虚拟基类。
此外,如果您没有为IBase
添加serialization_support免费功能,您将无法序列化/反序列化IBase*
,只能IObject*
(虽然我认为除非您通过IBase
而不是IObject
来管理所有权,否则这很好。