Reputation: 396
I'm trying to build a class that will act as a base class for any type I want to serialize in a private project I'm doing.
I'm trying to make the class work with at least boost serialization archives and QDataStream by providing functionality for '<<' and '>>'. Any other stream that will work with the class is just a bonus.
Important: I'll probably only use QDataStream. I'm building this class more as a puzzle/opportunity to learn (which seems to work) so while I would appreciate even workarounds that completely deviate from this form, I would very much like it if things could work the way I wanted them to (as close as possible given the restrictions of the language, of course), gaining some knowledge on the way.
The class as I thought it would be:
#ifndef SERIALIZABLE_H
#define SERIALIZABLE_H
#include <QObject>
#include <QDataStream>
#include <boost/serialization/access.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/split_member.hpp>
// #include boost stl containers handlers...
class Serializable : public QObject
{
Q_OBJECT
template <typename Archive>
virtual void Serializable_save( Archive &out, const quint32 p_version = 0 ) = 0;
template <typename Archive>
virtual void Serializable_load( Archive &in, const quint32 p_version = 0 ) = 0;
quint32 m_ID;
quint16 m_version;
public:
explicit Serializable( QObject *parent = 0 ) : QObject( parent ) {}
BOOST_SERIALIZATION_SPLIT_MEMBER()
template <typename Archive>
void save( Archive &out, const quint32 p_version = 0 )
{
out << m_ID << m_version;
Serializable_save( out, p_version );
}
template <typename Archive>
void load( Archive &in, const quint32 p_version = 0 )
{
in >> m_ID >> m_version;
Serializable_load( in, p_version );
}
quint32 ID() const;
void setID( const quint32 &ID );
quint16 version() const;
void setVersion( const quint16 &version );
};
template <typename Archive>
Archive &operator << ( Archive &out, const Serializable &module )
{
module.save( out );
return out;
}
template <typename Archive>
Archive &operator >> ( Archive &in, Serializable &module )
{
module.load( in );
return in;
}
#endif // SERIALIZABLE_H
I immediately discovered that virtual templates are not allowed and met the new term (for me) "type erasure".
I tried to employ type erasure after reading this article: On the Tension Between Object-Oriented and Generic Programming in C++ and What Type Erasure Can Do About It (up to and including "Beyond boost::any")...
Unsuccessfully.
A few notes:
Serializable_save & Serializable_load are part a naming convention that goes down the inheritance and allows for multilevel NVI. (Multilevel NVI is just a name I gave for the concept of finalizing the virtual function inherited from the base class and providing a new virtual function for inheritors. Allowing for a set of actions to always take place all the way down the inheritance chain) That means that an inheriting class that is not an end class will look like this:
#ifndef DATAMODULE_I_H
#define DATAMODULE_I_H
#include <StorageGateway/serializable.h>
class DataModule_I : public Serializable
{
template <typename Archive>
virtual void DataModule_I_save( Archive &out ) = 0;
template <typename Archive>
virtual void DataModule_I_load( Archive &in ) = 0;
template <typename Archive>
virtual void Serializable_save( Archive &out ) final
{
// Some preconditions.
DataModule_I_save( out );
// Some postconditions.
}
template <typename Archive>
virtual void Serializable_load( Archive &in ) final
{
// Some preconditions.
DataModule_I_load( in );
// Some postconditions.
}
public:
explicit DataModule_I( const quint32 ID, QObject *parent = 0 );
};
#endif // DATAMODULE_I_H
My next attempt was to bury the 'Archive' templates inside a StreamWrapper class (similar to type erasure but not quite it) in order to eliminate the immediate need for a template and pass the compiler's no-go concerning virtual templates' infinity problem.
Of course, that did not work. As I ended up needing to specify the template types which is exactly the opposite of what I was trying to achieve.
I'm using C++14 if it matters (between 11 and 14, I mean).
I still assume Type Erasure is the answer. I just don't understand how to use it.
So,
EDIT: I think this might be a solution but I can`t properly test it yet.
Upvotes: 1
Views: 107