Russian Qt Forum

Программирование => Общий => Тема начата: Igors от Май 10, 2016, 13:51



Название: Наследование template
Отправлено: Igors от Май 10, 2016, 13:51
Добрый день

Название темы корявое (не нашел лучшего), пример: вот сейчас я рисую некий template
Код
C++ (Qt)
template <class T>
struct MyTemplateClass {
 
 T * Get( size_t );
 void Set( T *, size_t );
 
 void Read( MyStream & );
 void Write( MyStream & ) const;
};
Первые 2 метода универсальны (не завязаны на др классы), а следующие 2 не очень - совсем необязательно что и в др проекте будет MyStream. Может в данном случае лучше сделать внешние операторы << и >>, но это частное решение. А как в общем случае сделать аккуратнее?

Спасибо


Название: Re: Наследование template
Отправлено: ssoft от Май 10, 2016, 14:48
В любом случае необходимо операции сериализации отделять от данных.
Т.е. такие структуры должны содержать информацию только о себе и методы для ее изменения.
О способе использования (в т.ч. способе сериализации) данные ни чего знать не должны.

Существует известная проблема, связанная с правами доступа к внутренним полям, но если использовать только public доступ, то таких проблем не возникнет.

Т.о. методы Read и Write должны быть внешними по отношению к MyTemplateClass и могут быть реализованы, например, через специализацию шаблона

Код
C++ (Qt)
template< class T >
struct Serializer {};
 
template< class T >
struct Serializer< MyTemplateClass< T > >
{
   static bool read ( MyStream &, MyTemplateClass< T > & );
   static bool write ( MyStream &, const MyTemplateClass< T > & );
};
 
struct Serializer< int >
{
   static bool read ( MyStream &, int & );
   static bool write ( MyStream &, int );
};
 
 

и т.д.

Вместе с методами сериализации можно и операторы <<,>> реализовать.

Код
C++ (Qt)
template < class T >
MyStream & operator << ( MyStream & stream, const T & value )
{
   Serializer< T >::write( stream, value );
   return stream;
}
 
template < class T >
MyStream & operator >> ( MyStream & stream, T & value )
{
   Serializer< T >::read( stream, value );
   return stream;
}
 

или что-то еще, не принципиально.


Название: Re: Наследование template
Отправлено: Igors от Май 11, 2016, 08:15
В любом случае необходимо операции сериализации отделять от данных.
Т.е. такие структуры должны содержать информацию только о себе и методы для ее изменения.
О способе использования (в т.ч. способе сериализации) данные ни чего знать не должны.
Не раз слышал эту "официальную доктрину", но мне она представляется спорной. Да, это убирает "зависимость", но ценой полного развала инкапсуляции - кому как не самому объекту знать как себя сохранять? Давно хотел попробовать такой вариант
Код
C++ (Qt)
template <class T>
struct MyTemplateClass {
 
 T * Get( size_t );
 void Set( T *, size_t );
 
 template <class Stream>
 void Read( Stream & );
 
 template <class Stream>
 void Write( Stream & ) const;
};
Что здесь плохого?


Название: Re: Наследование template
Отправлено: Racheengel от Май 11, 2016, 08:59
Мне тоже непонятна идея с разделением. Вернее, ее практический смысл.
Каким образом объект будет менять закрытые данные? И в чем преимущества подобного деления?
Если непременно хочется писать в несколько разных форматов, то объект все равно должен уметь выгрузить свои данные и восстановить их. В общем случае он может сериализироваться в какой-либо intermediate формат (например, в QDaraStream), а уже внешний сериалайзер может делать с этими данными что угодно. Но непосредственно сериализация данных должна быть частью объекта.


Название: Re: Наследование template
Отправлено: m_ax от Май 11, 2016, 09:34
Цитировать
Мне тоже непонятна идея с разделением. Вернее, ее практический смысл.
Вот сравните:
Код
C++ (Qt)
ObjA objA;
ObjB objB;
ObjC objC;
ObjD objD;
 
stream out;
objA.write(out);
out << objB;
objC.Write(out);
objD.write2Stream(out);
 

И это:
Код
C++ (Qt)
ObjA objA;
ObjB objB;
ObjC objC;
ObjD objD;
 
stream out;
 
out << objA << objB << objC << objD;
 

Цитировать
Каким образом объект будет менять закрытые данные?
Объявляете операторы чтения, записи друзьями.




Название: Re: Наследование template
Отправлено: Igors от Май 11, 2016, 14:59
Вот сравните:
Код
C++ (Qt)
...
stream out;
objA.write(out);
out << objB;
objC.Write(out);
objD.write2Stream(out);
 
Ну это можно пережить. Хужее если так
Код
C++ (Qt)
std::vector<Object1> arr1;
std::vector<Object2> arr2;
 
И выясняется что если методы записи/чтения Object1 и Object2 не совпадают, то обобщить (де) сериализацию контейнера не удастся, придется возюкаться с каждым. При этом операторы << и >> ничем не лучше, просто они традиционны/каноничны, поэтому лучше их придерживаться.


Название: Re: Наследование template
Отправлено: Igors от Май 11, 2016, 15:09
Давно хотел попробовать такой вариант
Код
C++ (Qt)
 template <class Stream>
 void Read( Stream & );
 
Сегодня попробовал и остался недоволен. Все хорошо пока (де)сериализация сводится к << и >>, на что я собсно и рассчитывал. Но вот как только что-то другое.. Напр у меня данные часто пишутся: идентификатор + длина + сами данные. А значит нужно подсчитать длину (в памяти или по файлу). Помещая этот код в тело template-метода я фактически признаю что пишу в конкретный (бинарный) поток, прощай общность.


Название: Re: Наследование template
Отправлено: m_ax от Май 11, 2016, 16:57
Цитировать
Но вот как только что-то другое.. Напр у меня данные часто пишутся: идентификатор + длина + сами данные. А значит нужно подсчитать длину (в памяти или по файлу). Помещая этот код в тело template-метода я фактически признаю что пишу в конкретный (бинарный) поток, прощай общность.
Поэтому специализируете serializer, как уже было предложено под конкретные форматы..
Код
C++ (Qt)
template <class>
struct serializer {};
 
template <>
struct serializer<XMLStream>
{
   template <class T>
   static bool read(XMLStream & in, T & data) {}
 
   template <class T>
   static bool write(XMLStream & out, const T & data) {}
}
 
 
template <class Stream>
 void Read( Stream & in)
{
     serializer<Stream>::read(in, *this);
}
 
 


Название: Re: Наследование template
Отправлено: Racheengel от Май 11, 2016, 18:13
Объявляете операторы чтения, записи друзьями.

Но в любом случае придется расширять классы, чтобы сделать это. Почему бы сразу не встроить в них универсальную сериализацию в стримовый формат?


Название: Re: Наследование template
Отправлено: m_ax от Май 11, 2016, 18:43
Цитировать
Но в любом случае придется расширять классы, чтобы сделать это.
Вы что имеете в виду под расширением? Я имел ввиду как то  так:
Код
C++ (Qt)
template <class T>
class SomeClass
{
public:
   template <class Stream, class R>
   friend Stream & operator<<(Stream &, const SomeClass<R> &);
 
   template <class Stream, class R>
   friend Stream & operator>>(Stream &, SomeClass<R> &);
 
private:
   T data;
};
 
 
template <class Stream, class R>
Stream & operator<<(Stream & out, const SomeClass<R> & obj)
{
   out << obj.data;
   return out;
}
 
template <class Stream, class R>
Stream & operator>>(Stream & in, SomeClass<R> & obj)
{
   in >> obj.data;
   return in;
}
 
 
 
int main()
{
   SomeClass<int> obj;
 
   std::cin >> obj;
   std::cout << obj;
 
   return 0;
}
 
 

Цитировать
Почему бы сразу не встроить в них универсальную сериализацию в стримовый формат?
Потому что во-первых, в этом нет особого смысла, а во-вторых, например, как вы запишите в поток какой-нибудь контейнер?
Я вот так вот:
Код
C++ (Qt)
QVector<SomeClass> vector;
 
out << vector;
 
А вы? Будете цикл организовывать?


Название: Re: Наследование template
Отправлено: __Heaven__ от Май 12, 2016, 09:46
А каким образом реализация и объявления должны быть разбросаны по файлам?
Со структурой понятно, что она должна быть в MyStruct.h и MyStruct.cpp. А как быть с методами записи в потоки? Как их распределять так, чтобы сохранялась переносимость между проектами?


Название: Re: Наследование template
Отправлено: Racheengel от Май 12, 2016, 10:00
Потому что во-первых, в этом нет особого смысла, а во-вторых, например, как вы запишите в поток какой-нибудь контейнер?

Ну, по сути, Вы делаете то же самое, но с помощью операторов :)
Я имел в виду такой вариант:

class MyClass
{
public:
   virtual void storeData(QDataStream& v) const;
   virtual void restoreData(QDataStream& v);
...
}

и далее так:

void MyClass::storeData(QDataStream& v) const
{
  v << myData1 << myData2;
}


void MyClass::restoreData(QDataStream& v)
{
  v >> myData1;
  v >> myData2;
}

а уже внешний контроллер может делать с v все, что захочет:

MyClass c;
...
QByteArray data;
QDataStream v(&data);
c.storeData(v);

QSettings set(...);
set.setValue("MyClassData", v);
...


Название: Re: Наследование template
Отправлено: m_ax от Май 12, 2016, 10:52
Цитировать
А каким образом реализация и объявления должны быть разбросаны по файлам?
Как решите, так и будет) Если друзья шаблонные, то обычно в том же хедере и реализация.

Цитировать
Ну, по сути, Вы делаете то же самое, но с помощью операторов
Конечно)
Только я всё равно не вижу чем лучше функции члены для чтения записи, чем специально отведённые для этого операторы?
Когда я имею дело с каким-либо классом я в первую очередь буду ожидать от него следующего стандартного поведения (когда речь идёт о чтении/записи)
Код
C++ (Qt)
out << obj;
in >> obj;
 

а не искать у него методов типа Write/storeData и т.д..  :)


Название: Re: Наследование template
Отправлено: Igors от Май 12, 2016, 11:01
Поэтому специализируете serializer, как уже было предложено под конкретные форматы..
Код
C++ (Qt)
template <class>
struct serializer {};
 
template <>
struct serializer<XMLStream>
{
   template <class T>
   static bool read(XMLStream & in, T & data) {}
 
   template <class T>
   static bool write(XMLStream & out, const T & data) {}
}
 
 
template <class Stream>
 void Read( Stream & in)
{
     serializer<Stream>::read(in, *this);
}
 
 
Ну а смысл этой городушки? Напр в роли Stream тот же QDataStream, методов read/write у него нет.

Я имел в виду такой вариант:

class MyClass
{
public:
   virtual void storeData(QDataStream& v) const;
   virtual void restoreData(QDataStream& v);
...
}

и далее так:

void MyClass::storeData(QDataStream& v) const
{
  v << myData1 << myData2;
}
Это сериализация для одного конкретного потока QDataStream, и этот код пролетает в проекте без Qt

как вы запишите в поток какой-нибудь контейнер?
Я вот так вот:
Код
C++ (Qt)
QVector<SomeClass> vector;
 
out << vector;
 

А вы? Будете цикл организовывать?
А за Вас кто это будет делать? Вообще цепочка операторов << и >> это самый низкий уровень когда данные следуют в известном порядке. А хотя бы уровнем выше их уже надо идентифицировать, как Вы будете это делать?


Название: Re: Наследование template
Отправлено: m_ax от Май 12, 2016, 11:49
Цитировать
А за Вас кто это будет делать?
Да это уже сделано. Посмотрите на QVector, например. Теперь чтоб его вместо одного движения записать в поток, придётся для каждого элемента вызывать не стандартные методы.
Зачем?
 
Цитировать
А хотя бы уровнем выше их уже надо идентифицировать, как Вы будете это делать?
А какое это имеет отношение к вопросу об использовании операторов<<>> vs мемберов?


Название: Re: Наследование template
Отправлено: Racheengel от Май 12, 2016, 12:12
Когда я имею дело с каким-либо классом я в первую очередь буду ожидать от него следующего стандартного поведения (когда речь идёт о чтении/записи)
Код
C++ (Qt)
out << obj;
in >> obj;
 

а не искать у него методов типа Write/storeData и т.д..  :)

Что значит "стандартное" поведение? В любом случае Вам придется для каждого поддерживаемого формата перегружать операторы. Сериализация в QDataStream будет отличаться от std::stream, таким образом, в любом случае obj должен явно поддерживать оба контейнера. Поэтому особых преимуществ, честно говоря, не вижу.

Что еще важно - как по мне, если методы определены явно в классе - это повышает читабельность, т.к. человек сразу видит его интерфейс, и то, с чем "умеет" работать класс. В этих же методах явно определяется порядок сериализации и десериализации. А наличие внешних сериалайзеров и их дополнительные специализации - это вводит дополнительные сущности и в итоге усложняет понимание системы классов.


Название: Re: Наследование template
Отправлено: m_ax от Май 12, 2016, 12:45
Цитировать
Что значит "стандартное" поведение?
Я вот о чём:
Код
C++ (Qt)
QVector<MyClass> vector;
out << vector;
 
QVector<YourClass> vector;
for (auto & elem : vector)
{
   elem.write(out);
}
 
Я выбираю первый вариант.


Название: Re: Наследование template
Отправлено: Igors от Май 12, 2016, 13:07
Я вот о чём:
Код
C++ (Qt)
QVector<MyClass> vector;
out << vector;
...
 
Я выбираю первый вариант.
Какой Вы быстрый :) А что бум делать если первый вариант вдруг не устраивает? См напр здесь (http://www.prog.org.ru/index.php?topic=29894.msg219900#msg219900).

Наивно полагать что нарисовав << и >> мы уже решили все проблемы :) Это даже не пол-дела. В действительности поток I/O должен иметь какую-то организацию, а не просто "идущие подряд данные" с которыми потом хрен разберешься.

В любом случае Вам придется для каждого поддерживаемого формата перегружать операторы. Сериализация в QDataStream будет отличаться от std::stream,
Да, именно. Ну может пример с бинарной/текстовой сериализацией проще и выразительнее. В первом случае нужно писать ID и длины, во втором нужны имена и правила разбивки на строки

Что еще важно - как по мне, если методы определены явно в классе - это повышает читабельность, т.к. человек сразу видит его интерфейс, и то, с чем "умеет" работать класс. В этих же методах явно определяется порядок сериализации и десериализации. А наличие внешних сериалайзеров и их дополнительные специализации - это вводит дополнительные сущности и в итоге усложняет понимание системы классов.
Существенно усложняет - но другого не видно, иначе никак не изолировать инклуды ненужных классов.


Название: Re: Наследование template
Отправлено: Racheengel от Май 12, 2016, 14:37
Я выбираю первый вариант.

Но это как раз и означает, что операторы >> << должны быть определены в контексте MyClass, т.к. сериализация QVector<T> явно требует этого. И для этого ВНЕШНИЕ классы в принципе не нужны.


Название: Re: Наследование template
Отправлено: m_ax от Май 12, 2016, 16:05
Цитировать
Но это как раз и означает, что операторы >> << должны быть определены в контексте MyClass, т.к. сериализация QVector<T> явно требует этого
Да, и я  реализую их вместо мемберов write/read. 


Название: Re: Наследование template
Отправлено: ssoft от Май 12, 2016, 17:47
Могу рассказать, как у нас решена задача сериализации в общем виде, причем с поддержкой версий структур данных для возможной бинарной совместимости.

Методы сериализации реализуются с помощью специализаций шаблонов

Код
C++ (Qt)
template < typename _Archive, typename _Value, int _version >
struct Serializer;
template < typename _Archive, typename _Value, int _version >
struct Loader;
template < typename _Archive, typename _Value, int _version >
struct Saver;
 

Для простоты объяснения понятие версии опущу.

По умолчанию реализация методов выглядит так

Код
C++ (Qt)
template < typename _Archive, typename _Value >
struct Serializer
{
   static void invoke ( _Archive & archive, _Value & value )
   {
       archive.corrupt( value );
   }
};
 
template < typename _Archive, typename _Value >
struct Loader
{
   static void invoke ( _Archive & archive, _Value & value )
   {
       Serializer< _Archive, _Value >::invoke( archive, value );
   }
};
 
template < typename _Archive, typename _Value >
struct Saver
{
   static void invoke ( _Archive & archive, const _Value & value )
   {
       Serializer< _Archive, _Value >::invoke( archive, const_cast< _Value & > ( value ) );
   }
};
 

По умолчанию, Loader и Saver вызывают один и тот же метод Serializer::invoke, и если методы сериализации и десериализации ни чем не отличаются (как это обычно и бывает), то достаточно специализировать только Serializer. Если отличия существуют из-за геттеров и сеттеров, например, то специализировать придется и Loader, и Saver (Serializer специализировать в этом случае не требуется).

Предположим, что нам необходимо сериализовать структуру

Код
C++ (Qt)
struct SimpleStruct
{
   int m_int;
   double m_double1;
   double m_double2;
};
 

Для этого необходимо специализировать один единственный метод сериализации

Код
C++ (Qt)
template < typename _Archive >
struct Serializer< _Archive, SimpleStruct >
{
   static void invoke ( _Archive& archive, SimpleStruct & value )
   {
       archive
           .serialize( value.m_int )
           .serialize( value.m_double1 )
           .serialize( value.m_double2 );
   }
};
 

Таким образом, один метод реализует и сериализацию и десериализацию. Но как и куда?
Здесь определяющую роль играет понятие архива. Архив - это ваш произвольный класс реализующий интерфейс для сериализации

Код
C++ (Qt)
class OutputArchive
{
public:
   template < typename _Value >
   Archive & serialize ( const _Value & value );
 
   template < typename _Value >
   void corrupt ( const _Value & value, int version );
};
 

или для десериализации

Код
C++ (Qt)
class InputArchive
{
public:
   template < typename _Value >
   Archive & serialize ( _Value & value );
 
   template < typename _Value >
   void corrupt ( _Value & value );
};
 

Куда/откуда и как происходит сохранение/чтение параметров решает разработчик архива.

Архив может сериализовать в std::stream, в QDataStream, в QTextStream, в разных форматах - XML, JSON, BIN и т.п. и т.д.

Для сериализации данных контейнеров типа std::vector, QVector для этих типов так же необходимо реализовать специализацию Serializer.

Для наглядности - пример архива

Код
C++ (Qt)
 
class XmlOArchive
{
private:
   QTextStream m_stream;
 
public
   XmlOArchive ( QIODevice * device )
   : m_stream( device )
   {
       m_stream.setCodec( "utf-8" );
       // ...
   }
 
public:
   template < typename _Value >
   Archive & serialize ( _Value & value )
   {
       QByteArray open_tag = "<Value>"
       QByteArray close_tag = "</Value>";
       m_stream << open_tag;
       Loader< _Value >::invoke( *this, value );
       m_stream << close_tag;
       return *this;
   }
 
   // Specialize for all POD types, for example.
   Archive & serialize ( int value )
   {
       QByteArray open_tag = "<Int>"
       QByteArray close_tag = "</Int>";
       m_stream
           << open_tag
           << value
           << close_tag;
       return *this;
   }
 
   template < typename _Value >
   void corrupt ( _Value & value )
   {
        qWarning() << "Archive corrupted";
   }
}
 
 

Доступ к приватным членам, нужен не часто, при правильной реализации должно хватать API класса.
Но если этого нет, можно использовать friend зависимости.


Название: Re: Наследование template
Отправлено: m_ax от Май 12, 2016, 17:57
Т.е. это фактически boost'овский вариант сериализации? Или есть принципиальные отличия?


Название: Re: Наследование template
Отправлено: Igors от Май 13, 2016, 06:12
Могу рассказать, как у нас решена задача сериализации в общем виде, причем с поддержкой версий структур данных для возможной бинарной совместимости.
Да, обобщить "туды-сюды" у меня руки так и не дошли. Интересно, спасибо

Такой момент: вот "простейший пример"
Код
C++ (Qt)
stream << a << b << c;
И типа "ну вот и вся сериализация" (одним движением). Однако это предполагает жесткий, неизменный порядок следования данных, все они должны быть прочитаны. Также ничего не известно о типе данных, мы должны полагаться что a (b, c) - именно данные тех самых типов что мы хотим. Поэтому так работает хорошо для относительно простых, базовых структур. В более сложных случаях данные должны быть идентифицированы и могут быть сериализованы в различном порядке. Как Вы решаете эту проблему?  


Название: Re: Наследование template
Отправлено: ssoft от Май 13, 2016, 08:19
Т.е. это фактически boost'овский вариант сериализации? Или есть принципиальные отличия?

Да, вариант очень похож на boost). Отличия существуют в ведении версий структур данных.
У нас версия сериализации также является специализируемым параметром шаблона, а не входящим параметром в метод сериализации, как в boost. И по-умолчанию, используется самая последняя версия сериализации, а не 0, как в boost. Есть еще непринципиальные отличия - названия методов, использование операторов, но базовые концепции идентичны, а решения очень похожи.

Код
C++ (Qt)
stream << a << b << c;
И типа "ну вот и вся сериализация" (одним движением). Однако это предполагает жесткий, неизменный порядок следования данных, все они должны быть прочитаны. Также ничего не известно о типе данных, мы должны полагаться что a (b, c) - именно данные тех самых типов что мы хотим. Поэтому так работает хорошо для относительно простых, базовых структур. В более сложных случаях данные должны быть идентифицированы и могут быть сериализованы в различном порядке. Как Вы решаете эту проблему?   

1. Кроме линейной/последовательной сериализации, таким же образом решена задача параметрической сериализации.

Код
C++ (Qt)
template < typename _Archive, typename _Key , typename _Value >
struct Parametrizer
{
   static void invoke ( _Archive & archive, const _Key & key, _Value & value )
   {
       archive.parametrize( key, value );
   }
};
 

Выдержка из документации:

Цитировать
Механизмы сериализации можно разделить на:
* линейные
* параметрические

Линейный механизм сериализации подразумевает под собой последовательный порядок сохранения и восстановления данных. Другими словами, чтобы получить пятую запись необходимо восстановить и все четыре предыдущие. Параметрический механизм позволяет получить записи по уникальному параметру - ключу. Таким образом та же 5-я запись может быть получена из десериализованных данных сразу, минуя предыдущие четыре.

Преимуществом линейного способа сериализации является максимальная скорость сохранения/восстановления данных в их полном виде, так как в параметрической форме при восстановлении требуется осуществлять поиск необходимой записи по параметрическому ключу. Тем не менее, в параметрическом виде можно сериализовывать не все записи, а только часть, и иногда осуществлять сериализацию/десериализацию между разными типами.

2. Для понимания, какой тип сохраняется и какая версия метода сериализации используется, эту информацию можно записать в архив.

У нас архив устроен так, что сериализация/десериалзация данных происходит пакетами QByteArray. Зачитываем пакет, из пакета читаем тип данных и версию сериализации, если зачитать не смогли, то пакет может быть проигнорирован, или особо обработан, или процесс десериализации прерван.

Я не описал, как ведется версионность, но это оочень длинный пост получается. К слову сказать, чтобы не заниматься явной специализацией, предусмотрены макросы, упрощающие запись

Код
C++ (Qt)
// version enumerator
SERIALIZE_VERSIONS_BEGIN( SimpleStruct )
   SERIALIZE_VERSION( Version_0 )
SERIALIZE_VERSIONS_END
 
// serialize & deserialize
SERIALIZE_BEGIN( SimpleStruct, Version_0 )
{
   archive
       .serialize( value.m_int )
       .serialize( value.m_double1 )
       .serialize( value.m_double2 );
}
SERIALIZE_END
 


Название: Re: Наследование template
Отправлено: Igors от Май 15, 2016, 11:27
Цитировать
Параметрический механизм позволяет получить записи по уникальному параметру - ключу. Таким образом та же 5-я запись может быть получена из десериализованных данных сразу, минуя предыдущие четыре.
Может не самый лучший пример - не припомню когда мне была нужна только 5-я запись :) Зато
Код
C++ (Qt)
stream >> a >> b >> c;  // было так (версия 1.0)
stream >> a >> a1 >> b >> c;  // появилась новая структура (версия 1.1)
stream >> a >> b >> x >>  c;  // неизвестная структура "x" (версия 1.0 читает данные записанные в 2.0)
 
Таких случаев хоть отбавляй. Создавать новую "версию потока" на каждое изменение - ну не знаю, выглядит накладно.

Но все равно, основательно у Вас в конторе сделано. Спасибо


Название: Re: Наследование template
Отправлено: Racheengel от Май 15, 2016, 12:49
У нас десериализация работает в 2 этапа:
1. Выгребаются ВСЕ сохраненные параметры в двоичном виде в формате ключ.длина.значения и пакуются в мэп.
2. А уже из мэпа достаются нужные параметры по ключам.
В итоге и очередность не важна, и контент можно конвертировать из предыдущих версий.
Версия архива пишется в хэдер, т.к. она для всех данных едина.


Название: Re: Наследование template
Отправлено: ssoft от Май 15, 2016, 18:52
Может не самый лучший пример - не припомню когда мне была нужна только 5-я запись :)

Здесь имеется ввиду, что при изменении версии структуры, ее новые данные сериализуются по новым ключам, а удаленные можно не сериализовать совсем.
Тогда старое ПО легко может частично десериализовать новую структуру и не "сломается". В линейном случае так легко это не прокатит.
Хорошим примером такого подхода является protobuf.

Создавать новую "версию потока" на каждое изменение - ну не знаю, выглядит накладно.

Здесь не требуется создавать новую версию потока (архива), требуется изменить только метода сериализации для структуры, а версию структуры можно сохранять в сам архив.
Тогда при чтении по версии можно определить, может ли структура быть десериализована и каким способом.

У нас версию имеет не архив, а сама структура. Это может показаться накладно, однако такой подход обеспечивает бинарную совместимость между различными версиями ПО. Это важно, например, когда ПО состоит из множества разных процессов, взаимодействующих между собой, а при обновлении система не может быть остановлена.

У нас десериализация работает в 2 этапа:
1. Выгребаются ВСЕ сохраненные параметры в двоичном виде в формате ключ.длина.значения и пакуются в мэп.
2. А уже из мэпа достаются нужные параметры по ключам.
...

Это и есть параметризация, когда данные пакуются по ключам.


Название: Re: Наследование template
Отправлено: _Bers от Май 17, 2016, 00:36
Добрый день

Название темы корявое (не нашел лучшего), пример: вот сейчас я рисую некий template
Код
C++ (Qt)
template <class T>
struct MyTemplateClass {
 
 T * Get( size_t );
 void Set( T *, size_t );
 
 void Read( MyStream & );
 void Write( MyStream & ) const;
};
Первые 2 метода универсальны (не завязаны на др классы), а следующие 2 не очень - совсем необязательно что и в др проекте будет MyStream. Может в данном случае лучше сделать внешние операторы << и >>, но это частное решение. А как в общем случае сделать аккуратнее?

Спасибо

это и есть общее и общепринятое решение.