Polymorphic Serialization w/ Boost
Asked Answered
A

1

3

I'm trying to (de)serialize a polymorphic vector, but have different issues with different attempts. The entire order of events are:

  • Serialize a Polymorphic Vector on Server Side
  • Send the serialized string over the network
  • De-serialize to a new Polymorphic Vector on Client Side
  • Edit Data in Vector (includes adding, editing and deleting) on Client Side
  • Serialize the edited Polymorphic Vector on Client Side
  • Send the new serialized string over the network
  • De-serialize the new Polymorphic Vector on Server Side <--- This is where my problem lies

I have Class Derived (and DerivedB, DerivedC etc) which derives from Class Base & a Class LotsOfBases which holds a Vector of Virtual Base.

Although, I don't see how this could be causing the issue - I believe my issue is because the objects in the Vector coming from the Server are in a particular order (AAABBCCCCD) and when they come back they are in a random order and may have different quantities of derived classes (ABABCDDDA).

Below are my failed attempts. Using method 2 below, if I am lucky I can send information back and forth (if class order remains the same), but when the class types change order, the problems begin to occur.

Code Used & Compile/Runtime Error:

  1. Compiles with no additions of course, but I get RunTime issues as Boost doesn't know which class is which... So I tried:

  2. ar.template register_type<Derived>() ; - Registering Class in "LotsOfBases.h"'s Serialize Function and got the following when called at RunTime: Error @ RunTime: what(): Input Stream Error - This is where I've had most success and what is mainly mentioned above.

  3. ar.register_type<static... But I get compile errors stating its a function (saw this else where on StackOverflow

  4. BOOST_CLASS_EXPORT(Derived) ; At the end of the ".h" file which gives n warnings for each different sub-class of Base and fails to compile. Error: multiple definition of ``boost::archive::detail::extra_detail::init_guid<Derived>::g'

  5. I tried to register the classes with the archiver in the main where LotsOfBases gets Deserialised. Compiler warnings

  6. BOOST_CLASS_EXPORT_IMPLEMENT(TextQuestion) from Exporting Class Serialization - Same errors as 6 iirc.

  7. The examples above without links are from my trolling through ~30 pages on StackOverflow which are similar but their solutions offered don't seem to work for me or are to do with Boost Serialization but somewhat irrelevant.

The following is a shortened version of my code (without edits used from elsewhere):

Classes' Code

LotsOfBases:

#include "s11n.h" //Import All Serialization Headers In Correct Order
namespace boost { namespace serialization { class access ; } }

class LotsOfBases
{
  public:
   std::vector<Base *> getAllBases() ;
  protected:
    std::vector<Base *> allBases() ;

    friend class boost::serialization::access ;

    template <typename Archive>
    void serialize(Archive& ar, const unsigned int /*version*/)
    {
        ar & allBases ;
    }

} ;

Base:

#include "s11n.h" //Import All Serialization Headers In Correct Order
namespace boost { namespace serialization { class access ; } }

class Base
{
  public: 
    Base() ;
    ~Base() ;

    virtual std::string getBaseLocation() ;
  protected:
    std::string baseLocation ;

    friend class boost::serialization::access ;

    template <typename Archive>
    void serialize(Archive& ar, const unsigned int /*version*/)
    {
        ar & baseLocation ;
    }
} ;

Derived

#include "s11n.h" //Import All Serialization Headers In Correct Order
namespace boost { namespace serialization { class access ; } }

class Derived
{
  public:
    Derived() ;

    bool getIsAttackableBase() ;
  private:
    bool isAttackableBase ;

    typedef Base _super;
    friend class boost::serialization::access ;

    template <typename Archive>
    void serialize(Archive& ar, const unsigned int /*version*/)
    {
        ar & boost::serialization::base_object<_super>(*this) ;
        ar & isAttackableBase ;
    }

I'm sure it shouldn't be so difficult. So, I guess my question is... What am I doing wrong? Where should I start reading/researching now?

Arnulfoarny answered 17/7, 2013 at 11:12 Comment(2)
BOOST_CLASS_EXPORT cannot be specified in an include file anymore. This is not reflected in the documentation! (Frankly, boost documentation sucks, and boost::serialize documentation sucks). Try moving it to an implementation file. Can you elaborate on BOOST_CLASS_EXPORT_KEY/BOOST_CLASS_EXPORT_IMPLEMENT? What have you done exactly, and what didn't work?Bergama
Also I hope this is not your real code (no virtual destructor, Derived does not inherit Base.)Bergama
S
3

I, as you, had searched for quite some time on how to serialize a polymorphic data, and your question was informative enough to help me understand my error, and correct yours. Since your classes are not fully implemented I provide a code example that achieves what you want. In my example

#pragma once
#include <iostream>
#include <vector>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream.hpp>

using namespace std;
class Parent; // Forward declares 
class Child;  // so my classes are in your order

Family is your LotsOfBases

class Family {

    friend class boost::serialization::access;

public:
    Family() { ; }
    ~Family() { ; }

    vector<Parent*> m_members;
    //////////////////////////////////////////////////////////////////////
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar.template register_type<Child>();
        ar & m_members;
    }
    //////////////////////////////////////////////////////////////////////

};

Parent is your Base

class Parent {

    friend class boost::serialization::access;

public:
    Parent() : m_name("") { ; } 
    Parent(string name) : m_name(name) { ; }
    ~Parent() { ; }

    virtual string GetName() { return m_name; }
private:

    string m_name;

    //////////////////////////////////////////////////////////////////////
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & m_name;
    }
    //////////////////////////////////////////////////////////////////////
};

Child is your Derived

class Child : public Parent {

    friend class boost::serialization::access;

public:
    Child() : Parent(), m_age(0) { ; }
    Child(string name, int id) : Parent(name), m_age(id) { ; }
    ~Child() { ; }
    int m_age;
private:
    //////////////////////////////////////////////////////////////////////
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & boost::serialization::base_object<Parent>(*this);
        ar & m_age;
    }
    //////////////////////////////////////////////////////////////////////
};

Main (for completeness)

int main() {

    Child *timmy = new Child("Timmy", 4);
    Family JohnsonFamily; 
    JohnsonFamily.m_members.push_back(timmy);

    // serialize object into a std::string
    std::string serial_str;
    boost::iostreams::back_insert_device<std::string> inserter(serial_str);
    boost::iostreams::stream<boost::iostreams::back_insert_device<std::string>> s(inserter);
    boost::archive::binary_oarchive oa(s);

    oa & JohnsonFamily;
    s.flush();

    // read object backout of standard string into new Family object
    boost::iostreams::basic_array_source<char> device(serial_str.data(), serial_str.size());
    boost::iostreams::stream<boost::iostreams::basic_array_source<char> > t(device);
    boost::archive::binary_iarchive ia(t);

    Family FosterFamily;
    ia & FosterFamily;

    auto baseptr = FosterFamily.m_members[0];
    auto child = dynamic_cast<Child*>(baseptr);
    if (child != nullptr) {
        cout << "Derived type infered from serialized base pointer." << endl;
        cout << child->GetName() << " is " << child->m_age << endl;
    }

    cin.get();
    return 0;
}

I noticed that your Derived doesn't actually inherit, which is certainly causing issues. Also, the key point is that if you have a polymorphic container in a class, then you must register each derived type in the that class (not in the base class). See my Family class above.

Hope this helps you.

Sanctimonious answered 29/11, 2015 at 5:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.