title: boost::serialization with immutable abstract base and virtual inheritance
body: <p>The code below is my current thinking to permit <code>boost::serialization</code> of an immutable abstract base with virtual inheritance. I hope I missed something and there is a simpler solution...?</p>
  
  <p>As it stands, it raises a few questions:</p>
  
  <ol>
  <li>Is the comment in <code>IObject::serialize</code> valid?</li>
  <li>The comment in <code>bs::save_construct_data</code> for <code>House</code> seems to indicate a bug in <code>boost::serialization</code>. Is that correct, or is there a better way to do this?</li>
  <li>Is there a more elegant way to deserialise <code>Building</code> then the <code>Deserialise</code> function combined with a protected constructor?</li>
  <li>The result of (3) is that another <code>Building</code> implementation will necessitate a chunk of duplicated code. I suspect this will require a bit of <a href="https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern" rel="nofollow">CRTP</a> to mitigate - any alternatives?</li>
  <li>How does one make this work if the virtual base contains data members? I suspect this is similar (or identical) to the <code>Building::Deserialise</code>.</li>
  </ol>
  
  <pre><code>
  
      #include &lt;fstream&gt;
      #include &lt;boost/archive/xml_oarchive.hpp&gt;
      #include &lt;boost/archive/xml_iarchive.hpp&gt;
      #include &lt;boost/serialization/export.hpp&gt;
  
      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 &lt;class Archive&gt;
        void serialize(Archive &amp;ar, const unsigned int version) {
          std::cout &lt;&lt; "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&lt;IObject, IBase&gt;(static_cast&lt;IObject *&gt;(nullptr),
                                                 static_cast&lt;IBase *&gt;(nullptr));
        }
  
      public:
        virtual ~IObject() {}
      };
  
      class IBuilding : public virtual IBase {
      private:
        friend class bs::access;
        template &lt;class Archive&gt;
        void serialize(Archive &amp;ar, const unsigned int version) {
          std::cout &lt;&lt; "Called IBuilding's serialize\n";
          bs::void_cast_register&lt;IBuilding, IBase&gt;(static_cast&lt;IBuilding *&gt;(nullptr),
                                                   static_cast&lt;IBase *&gt;(nullptr));
        }
  
      public:
        virtual ~IBuilding() {}
      };
  
      /* Tedious forward declarations to permit later friending. */
      class Building;
      class House;
  
      namespace boost {
      namespace serialization {
      template &lt;class Archive&gt;
      inline void save_construct_data(Archive &amp;ar, const Building *t,
                                      const unsigned int version);
      template &lt;class Archive&gt;
      inline void save_construct_data(Archive &amp;ar, const House *t,
                                      const unsigned int version);
      template &lt;class Archive&gt;
      inline void load_construct_data(Archive &amp;ar, House *t,
                                      const unsigned int version);
      }
      }
      /* Tedious forward declarations end. */
  
      class Building : public IBuilding, public IObject {
      private:
        friend class bs::access;
        template &lt;class Archive&gt;
        void serialize(Archive &amp;ar, const unsigned int version) {
          std::cout &lt;&lt; "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 &amp;bs::make_nvp("IObject", bs::base_object&lt;IObject&gt;(*this));
          ar &amp;bs::make_nvp("IBuilding", bs::base_object&lt;IBuilding&gt;(*this));
        }
  
        template &lt;class Archive&gt;
        inline friend void bs::save_construct_data(Archive &amp;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 &amp;members)
            : weight_(members.weight_), height_(members.height_) {}
        template &lt;class Archive&gt; const Members Deserialise(Archive &amp;ar) const {
          double weight;
          double height;
          ar &gt;&gt; bs::make_nvp("weight_", weight) &gt;&gt; bs::make_nvp("height_", height);
          return {weight, height};
        }
  
      public:
        bool operator==(const Building &amp;other) const {
          return weight_ == other.weight_ &amp;&amp; height_ == other.height_;
        }
  
        virtual double Height() const = 0;
      };
  
      class House : public Building {
      private:
        template &lt;class Archive&gt;
        inline friend void bs::save_construct_data(Archive &amp;ar, const House *t,
                                                   const unsigned int version);
        template &lt;class Archive&gt;
        inline friend void bs::load_construct_data(Archive &amp;ar, House *t,
                                                   const unsigned int version);
  
        template &lt;class Archive&gt;
        explicit House(Archive &amp;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 &lt;class Archive&gt;
      inline void save_construct_data(Archive &amp;ar, const Building *t,
                                      const unsigned int version) {
        std::cout &lt;&lt; "Called Building's save_construct_data\n";
        ar &lt;&lt; make_nvp("weight_", t-&gt;weight_) &lt;&lt; make_nvp("height_", t-&gt;height_);
      }
  
      template &lt;class Archive&gt;
      inline void bs::save_construct_data(Archive &amp;ar, const House *t,
                                          const unsigned int version) {
        std::cout &lt;&lt; "Called House's save_construct_data\n";
        const auto &amp;base = base_object&lt;const Building&gt;(*t);
        ar &lt;&lt; make_nvp("Building", base);
        // ar &lt;&lt; make_nvp("Building", &amp;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, &amp;base, version);
      }
  
      template &lt;class Archive&gt;
      inline void bs::load_construct_data(Archive &amp;ar, House *t,
                                          const unsigned int version) {
        std::cout &lt;&lt; "Called House's load_construct_data\n";
        ar &gt;&gt; make_nvp("Building", base_object&lt;Building&gt;(*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 &lt;&lt; house.Height() &lt;&lt; "\n";
        const IObject *iHouse = &amp;house;
  
        if (save_first) {
          std::ofstream ofs(file_name);
          boost::archive::xml_oarchive oa(ofs);
          oa &lt;&lt; BOOST_SERIALIZATION_NVP(iHouse);
        }
  
        IBuilding *iHouse2;
        {
          std::ifstream ifs(file_name);
          boost::archive::xml_iarchive ia(ifs);
          ia &gt;&gt; BOOST_SERIALIZATION_NVP(iHouse2);
        }
  
        if (dynamic_cast&lt;const Building &amp;&gt;(*iHouse) ==
            dynamic_cast&lt;const Building &amp;&gt;(*iHouse2))
          std::cout &lt;&lt; "ok\n";
        else
          std::cout &lt;&lt; "uh oh\n";
  
        return 0;
      }
  
  </code></pre>