Reputation: 13
I've been struggling the past few weeks to solve an issue I am having with serializing some data using boost::serialization.
I am trying to implement a tabbed editor that utilizes a client/server architecture for a project I am working on. The current design works like this:
DerivedTab extends from a base Tab class. (For this example question I have chosen not to include examples of the DerivedTab classes.).
Tab class extends AttributeContainer class which contains a map of string attribute names to AttributeBase*.
Attribute is a templated class that extends AttributeBase. It is intended to be used as a generic data type that can hold the value any concrete data type.
And finally, AttributeBase derives from NetworkSerializable which is a pure abstract base class used as a base object type to identify the contract that objects that can be serialized over a network connection must follow.
All of this is being compiled into a library which is then statically linked into my main application. As you can see, there is quite a bit of indirection going on and quite a few pitfalls that I'm trying to work around with serializing with boost::serialization. I've stripped out all additional code not pertinent to getting these classes to serialize. However, the example code is still quite lengthy.
main.cpp:
#include <sstream>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include "Tab.h"
using namespace test;
int main(int argc, char **argv)
{
std::ostringstream oarchiveStream;
boost::archive::text_oarchive outputArchive(oarchiveStream);
Tab* tab = new Tab("temp");
bool tempBool = true;
tab->RegisterAttribute("tempBool", "a temp boolean", &tempBool);
std::string tempString("1234");
tab->RegisterAttribute("tempString", "a temp string", &tempString);
outputArchive << tab;
}
Tab.h:
#ifndef __TAB_H__
#define __TAB_H__
#include "AttributeContainer.h"
#include <boost/serialization/base_object.hpp>
namespace test
{
class Tab : public AttributeContainer
{
friend class boost::serialization::access;
public:
Tab(const std::string tabName);
virtual ~Tab();
protected:
Tab();
template<class archive>
inline void serialize_attributes(archive& ar, const unsigned int version)
{
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(AttributeContainer);
ar & boost::serialization::make_nvp("TabName", _tabName);
}
virtual void serialize(boost::archive::text_oarchive& oa, const unsigned int version);
virtual void serialize(boost::archive::text_iarchive& ia, const unsigned int version);
private:
std::string _tabName;
};
} // namespace test
BOOST_CLASS_EXPORT_KEY(test::Tab);
#endif // #ifndef __TAB_H__
Tab.cpp:
#include "Tab.h"
BOOST_CLASS_EXPORT_IMPLEMENT(test::Tab);
using namespace test;
Tab::Tab(const std::string tabName) : _tabName(tabName)
{
}
Tab::~Tab()
{
}
Tab::Tab() : _tabName("")
{
}
void Tab::serialize(boost::archive::text_oarchive& oa, const unsigned int version)
{
std::cout << "Tab::serialize" << std::endl;
serialize_attributes(oa, version);
}
void Tab::serialize(boost::archive::text_iarchive& ia, const unsigned int version)
{
serialize_attributes(ia, version);
}
AttributeContainer.h:
#ifndef __ATTRIBUTE_CONTAINER_H__
#define __ATTRIBUTE_CONTAINER_H__
#include "NetworkSerializable.h"
#include <boost/serialization/map.hpp>
#include "Attribute.h"
namespace test
{
class AttributeContainer : public NetworkSerializable
{
friend class boost::serialization::access;
public:
std::map<std::string, AttributeBase*> _attributes;
AttributeContainer() {};
virtual ~AttributeContainer() {};
template <typename _T>
void RegisterAttribute(const std::string& name, const std::string& description, _T* var)
{
std::map<std::string, AttributeBase*>::const_iterator pos;
if ( (pos = _attributes.find(name)) == _attributes.end() )
{
Attribute<_T>* attribute = new Attribute<_T>(name, description, var);
_attributes.insert(std::map<std::string, AttributeBase*>::value_type(name, attribute));
}
};
template <class archive>
inline void serialize_attributes(archive& ar, const unsigned int version)
{
ar & _attributes;
};
virtual void serialize(boost::archive::text_oarchive& oa, const unsigned int version);
virtual void serialize(boost::archive::text_iarchive& ia, const unsigned int version);
}; // end class AtributeContainer
} // end namespace test
BOOST_CLASS_EXPORT_KEY(test::AttributeContainer);
#endif // #ifndef __ATTRIBUTE_CONTAINER_H__
AttributeContainer.cpp:
#include "AttributeContainer.h"
BOOST_CLASS_EXPORT_IMPLEMENT(test::AttributeContainer);
using namespace test;
void AttributeContainer::serialize(boost::archive::text_oarchive& oa, const unsigned int version)
{
std::cout << "AttributeContainer::serialize" << std::endl;
serialize_attributes(oa, version);
}
void AttributeContainer::serialize(boost::archive::text_iarchive& ia, const unsigned int version)
{
serialize_attributes(ia, version);
}
Attribute.h:
#ifndef __ATTRIBUTE_H__
#define __ATTRIBUTE_H__
#include "AttributeBase.h"
namespace test
{
template <typename _T>
class Attribute : public AttributeBase
{
friend class AttributeContainer;
friend class boost::serialization::access;
public:
typedef _T AttributeType;
Attribute() : _data(0) {}
Attribute(const std::string& name, const std::string& description, AttributeType* var) : _data(var)
{
_name = name;
_description = description;
}
virtual ~Attribute() {}
protected:
AttributeType* _data;
template <class archive>
inline void serialize_base(archive& ar, const unsigned int version)
{
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(AttributeBase);
ar & boost::serialization::make_nvp("Value", *_data);
}
virtual void serialize(boost::archive::text_oarchive& oa, const unsigned int version)
{
std::cout << "Attribute::serialize" << std::endl;
serialize_base(oa, version);
}
virtual void serialize(boost::archive::text_iarchive& ia, const unsigned int version)
{
serialize_base(ia, version);
}
};
} // namespace test
BOOST_CLASS_EXPORT_KEY(test::Attribute<bool>);
BOOST_CLASS_EXPORT_KEY(test::Attribute<std::string>);
#endif // #ifndef __ATRIBUTE_H__
Attribute.cpp:
#include "Attribute.h"
BOOST_CLASS_EXPORT_IMPLEMENT(test::Attribute<bool>);
BOOST_CLASS_EXPORT_IMPLEMENT(test::Attribute<std::string>);
using namespace test;
AttributeBase.h:
#ifndef __ATTRIBUTE_BASE_H__
#define __ATTRIBUTE_BASE_H__
#include "NetworkSerializable.h"
#include <string>
namespace test
{
class AttributeBase : public NetworkSerializable
{
friend class AttributeContainer;
friend class boost::serialization::access;
public:
AttributeBase();
virtual ~AttributeBase();
protected:
AttributeBase& operator=(const AttributeBase&);
AttributeBase(const AttributeBase&);
protected:
std::string _name;
std::string _description;
template<class archive>
inline void serialize_attributes(archive& ar, const unsigned int version)
{
ar & boost::serialization::make_nvp("Name", _name);
ar & boost::serialization::make_nvp("Description", _description);
}
virtual void serialize(boost::archive::text_oarchive& oa, const unsigned int version);
virtual void serialize(boost::archive::text_iarchive& ia, const unsigned int version);
}; // end class AttributeBase
} // end namespace test
BOOST_SERIALIZATION_ASSUME_ABSTRACT(test::AttributeBase);
BOOST_CLASS_EXPORT_KEY(test::AttributeBase);
#endif // #ifndef __ATTRIBUTE_BASE_H__
NetworkSerializable.h:
#ifndef __NETWORK_SERIALIZABLE_H__
#define __NETWORK_SERIALIZABLE_H__
#pragma warning(disable:4244)
#include <boost/shared_ptr.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/export.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
namespace test
{
class NetworkSerializable
{
friend class boost::serialization::access;
public:
typedef std::shared_ptr<NetworkSerializable> NetworkSerializablePtr;
NetworkSerializable() {};
protected:
virtual void serialize(boost::archive::text_oarchive& oa, const unsigned int version) = 0;
virtual void serialize(boost::archive::text_iarchive& ia, const unsigned int version) = 0;
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(NetworkSerializable);
} // namespace test
#endif // #ifndef __NETWORK_SERIALIZABLE_H__
I've tried to make the code as concise and minimal as possible to fully demonstrate the issue I am having.
The output from the provided code is:
Tab::serialize
Tab::serialize
When the output should be:
Tab::serialize
AttributeContainer::serialize
Attribute::serialize
AttributeBase::serialize
Attribute::serialize
AttributeBase::serialize
There is quite a bit of code here for someone to digest so I would be very thankful if anyone can offer any insight where I might have strayed along the boost serialization path.
Upvotes: 1
Views: 842
Reputation: 5118
In a nutshell: your serialize
member functions should not be virtual. Making them virtual results in the call to static_cast<AttributeContainer*>(this)->serialize(...)
, made by boost::serialization::base_object
inside of Tab::serialize_attributes
, landing back in Tab::serialize
through virtual function dispatch.
Here's a working single-file example based on your code:
namespace serial_test
{
using namespace std;
class NetworkSerializable {
friend class boost::serialization::access;
public:
typedef std::shared_ptr<NetworkSerializable> NetworkSerializablePtr;
NetworkSerializable() {};
protected:
// void serialize(boost::archive::text_oarchive& oa, const unsigned int version) = 0;
// void serialize(boost::archive::text_iarchive& ia, const unsigned int version) = 0;
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(NetworkSerializable);
class AttributeBase : public NetworkSerializable {
friend class AttributeContainer;
friend class boost::serialization::access;
public:
AttributeBase() {}
virtual ~AttributeBase() {}
protected:
std::string _name;
std::string _description;
template<class archive>
inline void serialize_attributes(archive& ar, const unsigned int version) {
ar & boost::serialization::make_nvp("Name", _name);
ar & boost::serialization::make_nvp("Description", _description);
}
void serialize(boost::archive::text_oarchive& oa, const unsigned int version) {
cout << "AttributeBase::serialize" << endl;
serialize_attributes(oa, version);
}
void serialize(boost::archive::text_iarchive& ia, const unsigned int version) {
serialize_attributes(ia, version);
}
}; // end class AttributeBase
template <typename _T>
class Attribute : public AttributeBase {
friend class AttributeContainer;
friend class boost::serialization::access;
public:
typedef _T AttributeType;
Attribute() : _data(0) {}
Attribute(const std::string& name, const std::string& description, AttributeType* var) : _data(var) {
_name = name;
_description = description;
}
virtual ~Attribute() {}
protected:
AttributeType* _data;
template <class archive>
void serialize_base(archive& ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(AttributeBase);
ar & boost::serialization::make_nvp("Value", *_data);
}
void serialize(boost::archive::text_oarchive& oa, const unsigned int version) {
std::cout << "Attribute::serialize" << std::endl;
serialize_base(oa, version);
}
void serialize(boost::archive::text_iarchive& ia, const unsigned int version) {
serialize_base(ia, version);
}
};
class AttributeContainer : public NetworkSerializable {
friend class boost::serialization::access;
public:
std::map<std::string, AttributeBase*> _attributes;
AttributeContainer() {};
virtual ~AttributeContainer() {};
template <typename _T>
void RegisterAttribute(const std::string& name, const std::string& description, _T* var) {
std::map<std::string, AttributeBase*>::const_iterator pos;
if ( (pos = _attributes.find(name)) == _attributes.end() ) {
Attribute<_T>* attribute = new Attribute<_T>(name, description, var);
_attributes.insert(std::map<std::string, AttributeBase*>::value_type(name, attribute));
}
};
template <class archive>
void serialize_attributes(archive& ar, const unsigned int version) {
ar & _attributes;
};
void serialize(boost::archive::text_oarchive& oa, const unsigned int version) {
std::cout << "AttributeContainer::serialize" << std::endl;
serialize_attributes(oa, version);
}
void serialize(boost::archive::text_iarchive& ia, const unsigned int version) {
serialize_attributes(ia, version);
}
}; // end class AtributeContainer
class Tab : public AttributeContainer {
friend class boost::serialization::access;
public:
Tab(const std::string tabName)
: _tabName(tabName) {}
virtual ~Tab() {}
protected:
Tab()
: _tabName("") {}
template<class archive>
inline void serialize_attributes(archive& ar, const unsigned int version) {
// ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(AttributeContainer);
ar & boost::serialization::base_object<AttributeContainer>(*this);
ar & boost::serialization::make_nvp("TabName", _tabName);
}
void serialize(boost::archive::text_oarchive& oa, const unsigned int version) {
std::cout << "Tab::serialize" << std::endl;
serialize_attributes(oa, version);
}
void serialize(boost::archive::text_iarchive& ia, const unsigned int version) {
serialize_attributes(ia, version);
}
private:
std::string _tabName;
};
void test() {
std::ostringstream oarchiveStream;
boost::archive::text_oarchive outputArchive(oarchiveStream);
Tab* tab = new Tab("temp");
bool tempBool = true;
tab->RegisterAttribute("tempBool", "a temp boolean", &tempBool);
std::string tempString("1234");
tab->RegisterAttribute("tempString", "a temp string", &tempString);
outputArchive << tab;
}
} // namespace serial_test
BOOST_SERIALIZATION_ASSUME_ABSTRACT(serial_test::AttributeBase);
BOOST_CLASS_EXPORT_KEY(serial_test::AttributeBase);
BOOST_CLASS_EXPORT_KEY(serial_test::Attribute<bool>);
BOOST_CLASS_EXPORT_KEY(serial_test::Attribute<string>);
BOOST_CLASS_EXPORT_IMPLEMENT(serial_test::Attribute<bool>);
BOOST_CLASS_EXPORT_IMPLEMENT(serial_test::Attribute<string>);
BOOST_CLASS_EXPORT_KEY(serial_test::Tab);
BOOST_CLASS_EXPORT_IMPLEMENT(serial_test::Tab);
Upvotes: 1