user2962533
user2962533

Reputation: 396

Virtual template workaround for multi level NVI

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

Answers (0)

Related Questions