Виртуальный шаблонный обходной путь для многоуровневого NVI
Я пытаюсь создать класс, который будет действовать как базовый класс для любого типа, который я хочу сериализовать в частном проекте, который я делаю.
Я пытаюсь заставить класс работать, по крайней мере, с усилением архивов сериализации и QDataStream, предоставляя функциональные возможности для << и '>>. Любой другой поток, который будет работать с классом, является просто бонусом.
Важно: я, вероятно, буду использовать только QDataStream. Я строю этот класс скорее как головоломку / возможность учиться (что, кажется, работает), поэтому, хотя я был бы признателен даже за обходные пути, которые полностью отклоняются от этой формы, мне бы очень понравилось, если бы все могло работать так, как я этого хотел. (как можно ближе, учитывая ограничения языка, конечно), получая некоторые знания в пути.
Класс, как я думал, будет:
#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
Я сразу обнаружил, что виртуальные шаблоны не разрешены, и встретил новый термин (для меня) "стирание типов".
Я попытался использовать стирание типов после прочтения этой статьи: " О противоречии между объектно-ориентированным и общим программированием в C++ и о том, что стирание типов может с этим поделать" (вплоть до "Beyond boost::any")...
Неудачно.
Несколько заметок:
Serializable_save и Serializable_load являются частью соглашения об именовании, которое сокращает наследование и допускает многоуровневый NVI. (Многоуровневый NVI - это просто имя, которое я дал для концепции завершения виртуальной функции, унаследованной от базового класса, и предоставления новой виртуальной функции для наследников. Возможность набора действий, которые всегда выполняются на протяжении всей цепочки наследования). означает, что наследующий класс, который не является конечным классом, будет выглядеть так:
#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
Моя следующая попытка состояла в том, чтобы похоронить шаблоны "Архив" внутри класса StreamWrapper (аналогично стиранию типов, но не совсем так), чтобы исключить насущную необходимость в шаблоне и пропустить бесконечную проблему компилятора в отношении отсутствия виртуальных шаблонов.
Конечно, это не сработало. В итоге мне пришлось указать типы шаблонов, что в точности противоположно тому, чего я пытался достичь.
Я использую C++14, если это имеет значение (между 11 и 14, я имею в виду).
Я все еще предполагаю, что Тип Erasure является ответом. Я просто не понимаю, как его использовать.
Так,
- Можно ли добиться желаемого поведения? Если да:
- Как я могу получить класс, который разрешит такое поведение "Multilevel NVI", когда виртуальные шаблоны недопустимы?
РЕДАКТИРОВАТЬ: Я думаю, что это может быть решением, но я пока не могу должным образом проверить его.