2016-02-05 34 views
0

下面的代碼是我目前的想法允許boost::serialization一個不變的抽象基礎與虛擬繼承。我希望我錯過了一些東西,並有一個更簡單的解決方案......?boost ::序列化與不可變的抽象基礎和虛擬繼承

因爲它的立場,它提出了幾個問題:

  1. 是在IObject::serialize評論是否有效?
  2. House中的評論bs::save_construct_data似乎表示boost::serialization中存在一個錯誤。這是正確的,還是有更好的方法來做到這一點?
  3. 是否有一個更優雅的方式來反序列化Building然後Deserialise函數結合受保護的構造函數? (3)的結果是另一個Building實現將需要大量重複的代碼。我懷疑這將需要一些CRTP緩解 - 任何替代方案?
  4. 如果虛擬基礎包含數據成員,那麼這是如何工作的?我懷疑這與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

IBase默認構造正確嗎?如果是這樣,你根本不需要考慮它,當然? –

+0

'IBase'默認構造正確。是的,也許這不應該成爲問題的一部分。我們的庫確實將它包含在序列化中,但是我會考慮改變它。想一想,這引出了一個問題:如果我們有一個像這樣的結構,我們想通過'IBase'串行化呢? – Rai

+0

根據文檔 –

回答

0

我不相信的評論是正確的。我相信void_cast_register是不必要的,因爲IBase被稱爲IObject的虛擬基類。

Futhermore,如果不加serialization_support免費功能IBase,你將無法連載/ deserialise的IBase*,只有IObject*(雖然我覺得這很好,除非你通過IBase而管理所有權比IObject)。