使用不可变抽象基和boost继承的boost :: serialization

时间:2016-02-05 14:46:27

标签: c++ boost-serialization

下面的代码是我目前的想法,即允许boost::serialization具有虚拟继承的不可变抽象基础。我希望我错过了一些更简单的解决方案......?

目前,它提出了几个问题:

  1. IObject::serialize中的评论是否有效?
  2. bs::save_construct_data House中的评论似乎表明boost::serialization中存在错误。这是正确的,还是有更好的方法来做到这一点?
  3. 是否有更优雅的方法来反序列化Building,然后将Deserialise函数与受保护的构造函数结合使用?
  4. (3)的结果是另一个Building实现将需要一大堆重复代码。我怀疑这需要一些CRTP来缓解 - 任何替代方案?
  5. 如果虚拟基础包含数据成员,那么如何使这项工作成功?我怀疑这与Building::Deserialise相似(或相同)。
  6. 
    
        #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;
        }
    
    

1 个答案:

答案 0 :(得分:0)

我不相信评论是正确的。我认为void_cast_register是不必要的,因为已知IBaseIObject的虚拟基类。

此外,如果您没有为IBase添加serialization_support免费功能,您将无法序列化/反序列化IBase*,只能IObject*(虽然我认为除非您通过IBase而不是IObject来管理所有权,否则这很好。