Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Февраль 06, 2013, 15:37



Название: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 06, 2013, 15:37
Добрый день

Навеяно этим
Не запихивайте всю структуру, да ещё по указателю в сеть. Ничего хорошего не выйдет. Делайте сериализацию для каждого поля в отдельности. И восстанавливайте на приёмной стороне точно также, по позициям.
Конечно это безусловно верно. Но вот полей может быть много. Да, придется все записать, но хотелось бы сделать это 1 раз в не 2. А то выходит
Код
C++ (Qt)
...
strm << mID;
strm << mName;
..  // еще десятки
 
// а на др стороне
...
strm >> mID;
strm >> mName;
..  
 
Ну и если чего-то изменилось, сбивать 2 копии утомительно. Как бы это дело обобщить?

Спасибо


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 06, 2013, 15:49
У структуры делать оператор. Тогда всё сведётся к out << myStruct;.

Хотя конечно в более крутом смысле вашего вопроса (как обычно), можно использовать любую библиотеку сериализация на манер s11n. Но писать операторы придётся в любом случае :)



Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 06, 2013, 15:57
У структуры делать оператор. Тогда всё сведётся к out << myStruct;.
Я и спрашиваю как избежать повтора довольно большого куска в операторах >> и << для большой структуры

Хотя конечно в более крутом смысле вашего вопроса (как обычно),
"Циля, он еще и поет"  :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 06, 2013, 16:02
Есть сомнительный вариант - хранить все структуры в виде объединения массива байтов и его представления в простых типах. Но сложные тут уже не сохранить, а ссылочные типы так вообще идут лесом.

Сомнительный - потому что я уже встречал программиста это использующего. Впечатлений много. Особенно после описания функции в 72 (Я ЗАПОМНИЛ!!!) строки, чтобы получить 3 поля структуры.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: alexis031182 от Февраль 06, 2013, 16:13
...
Ну и если чего-то изменилось, сбивать 2 копии утомительно. Как бы это дело обобщить?
Добрый день. Ну тут как бы всё равно речь о двух и более машинах, значит и мест правки кода будет несколько. Другое дело, что это, наверное, можно свести к одной либе, копия файла которой будет у сервера/серверов и клиентов.

Что касаемо отдельных полей, то всё равно необходимо хотя бы один раз поработать с каждым. Впрочем, тут по ситуации. Я согласен с Вересом, что можно попросту переопределить соответствующий оператор.

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


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 06, 2013, 17:02
Я и спрашиваю как избежать повтора довольно большого куска в операторах >> и << для большой структуры
Ручками, ручками :). А если ручками лень, то написать парсер, который по исходникам сгенерит сериализаторы. Которые потом все равно придется ручками править :D.

Если применительно к структурам struct, то, похоже, все печально - к каждому полю придется обращаться отдельно. Махинации с памятью чреваты, как отметил Верес.

Можно попробовать придумать и использовать другие конструкции, похожие на struct, которые лучше поддаются сериализации. Но это если будет позволительно такое в программе использовать. А то, возможно, придется и их в struct конвертировать, что возвращает нас к исходной проблеме.

В boost'е тоже какие-то сериализаторы встречались.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 06, 2013, 17:36
Ручками, ручками :). А если ручками лень, то написать парсер, который по исходникам сгенерит сериализаторы. Которые потом все равно придется ручками править :D.
Ручки у барышень которые не работают :) Неужто все так безнадежно? Ну почему напр не так
Код
C++ (Qt)
void MyStruct::ReadWrite( QDataStream & strm, bool modeWrite )
{
ReadWriteInt(strm, &mID, modeWrite);
ReadWriteQString(strm, &mName, modeWrite);
...
}


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 06, 2013, 17:47
Эм... Вы только что считай написали оператор Де/Сериализации :D

Правда довольно усложнённый на мой взгляд. Ибо все простые типы в Кудатастриме извлекаются легко и непринуждённо, а самопальные всё равно придётся ручками писать.

Объясню почему мне кажется усложнённым. Если делать операторами, будет сериализация/десериализация. Если делать как у вас, появляется флаг, который как гордый флаг варяга будет бродить по всем вашим функциям, внося дополнительные строки проверок и прочая.

PS кстати, а почему не так
Код:
void MyStruct::ReadWrite( QDataStream & strm, bool modeWrite )
{
if (modeWrite)
    out >>  this.intPoleMyStruct >> this.qstringPoleMyStruct ... ;
else
 ...
}


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 06, 2013, 18:12
Код
C++ (Qt)
void MyStruct::ReadWrite( QDataStream & strm, bool modeWrite )

Да, в один метод это лучше не пихать, и по вышеназванной причине тоже. И вообще, лучше не делать это методом структуры, а создавать отдельно. Как, например, и рекомендуется в QDataStream, операторами. Потому что, в общем случае, структуру не должно волновать, кто и куда захочет ее сериализовать. QDataStream - это, конечно, хорошо, но вдруг кому захочется сохранять/загружать/передавать в XML/JSON/YAML/по сети/как-нибудь еще. Или, наоборот, кто-нибудь не будет пользоваться QDataStream, зачем ему эти методы в структуре? :)

А написать отдельные методы на чтение и на запись, не самое страшное, что есть в сериализации :).


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 06, 2013, 18:46
ViTech продолжите свою мысль пожалуйста. Где же тогда создавать сериализацию и десериализацию? и псевдокод использования пожалуйста, если не сложно.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 06, 2013, 19:09
Я бы использовал отдельные файлы для каждого способа сериализаци для каждой структуры. Например:
Цитировать
IO/DataStream/MyStructRead.h
IO/DataStream/MyStructWrite.h
IO/DataStream/YourStructRead.h
IO/DataStream/YourStructWrite.h
IO/XML/MyStructRead.h
IO/XML/MyStructWrite.h
IO/XML/YourStructRead.h
IO/XML/YourStructWrite.h
Тот, кто будет читать/записывать, знает, каким способом он это сделает, подключит необходимые файлы и вызовет нужные функции. Может много хидеров подключить придется, зато ничего лишнего не будет. Можно и сгруппировать операции связанных структур в один файл, а не каждую в отдельный, но в общем смысл такой.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 06, 2013, 20:31
А написать отдельные методы на чтение и на запись, не самое страшное, что есть в сериализации :).
Ну ссылки и указатели конечно более серьезная проблема. Все же дублирование сотен строк кода  (структуры обычно развесистые) с заменой >> << туда-сюда сильно смахивает на copy/paste. Еще хуже когда в структуре что-то поменялось (пусть и номер версии на руках). От увлекающихся обобщенным программированием я ожидал чего-то типа такого
Код
C++ (Qt)
template <class Stream, class T>
void ReadWrite( Stream & strm, T * data, int mode )
{
switch (mode) {
 case mode_Read:
  strm >> *data;
  break;    
 
 case mode_Write:
  strm << *data;
  break;    
 
 case mode_Skip:
  ...
}
}
 

появляется флаг, который как гордый флаг варяга будет бродить по всем вашим функциям, внося дополнительные строки проверок и прочая.
Ну и что - переживу. В любом приложении много кто где бродит

PS кстати, а почему не так
Код:
void MyStruct::ReadWrite( QDataStream & strm, bool modeWrite )
{
if (modeWrite)
    out >>  this.intPoleMyStruct >> this.qstringPoleMyStruct ... ;
else
 ...
}
Это бессмысленно, так все равно read и write отдельно, просто в 1 ф-ции - все равно сбивать. А я хочу оформить весь содержательный I/O одним телом, а операторы << и >> записать по 1 строке, вызывая ReadWrite с разными mode


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: sergek от Февраль 06, 2013, 20:41
Конечно это безусловно верно. Но вот полей может быть много. Да, придется все записать, но хотелось бы сделать это 1 раз в не 2.
skip
Как бы это дело обобщить?
Рискну предложить свое решение – интерфейс удаленных вызовов, API. Объектное решение, работает уже лет 12 в одной биллинговой системе.
Общий подход следующий:
- архитектура клиент/сервер. Сервер запущен и слушает заданный порт, клиент соединяется с этим портом;
- обмен между клиентом и сервером осуществляется путем вызова особых функций на клиенте и получения результатов исполнения этих функций на сервере;
- аргументами функции и возвращаемым результатом служат указатели на объекты классов, порожденные от одного интерфейсного BoaInterface;
- объявления для классов и функций общие для клиента и сервера;
- реализации функций – свои для клиента и сервера:
  а) клиент выполняет передачу аргументов на сервер и прием результатов,
  б) сервер выполняет прием аргументов от клиента, выполнение бизнес-логики и передачу результатов клиенту.

Пример использования:
1) объявления классов и функций (общие для клиента и сервера):
Код:
// тестовый класс
class TestRecord : public BoaInterface
{
public:
    bool         boolField;
    char         charField;
    short        shortField;
    int          intField;
    long         longField;
    bigint       bigintField;
    float        floatField;
    double       doubleField;
    string       stringField;
    BoaDateTime  dateField;
 
    TestRecord();
};
// функция получения объекта
TestRecord* selRecord(TestRecord* rec);

2) реализация класса (тоже общая для клиента и сервера):
Код:
// инициализация членов Boa-объекта
TestRecord::TestRecord(){
    push(boolField,"boolField");
    push(charField,"charField");
    push(shortField,"shortField");
    push(intField,"intField");
    push(longField,"longField");
    push(bigintField,"bigintField");
    push(floatField,"floatField");
    push(doubleField,"doubleField");
    push(stringField,"stringField");
    push(dateField,"dateField",BoaDateTime::DateTime);
 }

3) реализация клиентской функции:

Код:
// обработка запроса одной записи
TestRecord* selRecord(TestRecord* rec){
    // создание и отправка командного объекта
    BoaCommand cmd("selRecord");
    if(cmd.send()==BOA_ERROR)
        return 0;

    // получение результата
    int ret=rec->receive();
    // возвращаемое значение
    if(ret==BOA_ERROR)
        return 0;
    else
        return rec;
}

4) реализация серверной функции:

Код:
// обработка запроса одной записи
TestRecord* selRecord(TestRecord* arg){
    // принятая команда
    BoaCommand* cmd=BoaCommandPtr(arg);

    // формирование данных для клиента
    TestRecord record;
    record["boolField"]=true;
    record["charField"]=char('c');
    record["shortField"]=1;
    record["intField"]=10;
    record["longField"]=100;
    record["bigintField"]=1000;
    record["floatField"]=0.1;
    record["doubleField"]=0.01;
    record["stringField"]="Тестовая запись";
    record["dateField"]=BoaDateTime::currentDateTime();

    // отправка результата клиенту
#if defined(__BORLANDC__) || defined(QT_WIN)
    // win-версия
    record.sendTo(cmd->hSocket);
#else
    // *nix-версия
    record.send();
#endif
    return 0;
}

В общем-то, и все. Вся сериализация скрыта в методах базового класса BoaInterface::send() и BoaInterface::receive(), сериализуются все основные типы и некоторые встроенные предопределенные типы.
Остальное одинаково для всех программ – организация соединения со стороны клиента, прослушивание порта со стороны сервера. Есть нюансы для win- и nix-реализации. Один раз пишется и переносится из проекта в проект.
Более того, процесс написания можно автоматизировать. В свое время даже хотел написать визард для генерации кода (stub/sckeleton), но, поскольку, писал все программы сам, для себя поленился. Есть встроенная поддержка BDE и PostgreSQL, правда, древней версии. Возможен callback из серверной функции на клиента.
Если заинтересует, библиотеку можно найти по адресу http://www.freesoft.ru/?id=671951 (http://www.freesoft.ru/?id=671951). Полная документация (руководство программиста, справочник), примеры для Borland C++Builder и Qt в Windows и Unix. Правда, давно не компилировал под nix-системами (последний раз проверял под FreeBSD 4.5 и Fedora 10 (или 12?).
Но если кого заинтересует, проверю в ubunta 12.04.01.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: sergek от Февраль 06, 2013, 20:53
Кстати, по указанному адресу есть и скомпилированные примеры, можно запустить и попробовать.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 06, 2013, 21:01
В принципе ничего нового, кроме шаблонного метода Igors.

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

// шаблоны понимаю с трудом :(

Другой вопрос, что замена месторасположение одного поля или добавление самописного типа приведёт в любом случае к "ручной" правке.

to Igors по поводу вашей реплики насчёт моего предложения:

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

Вопрос to Igors -
Немного не понял кстати по поводу вашего шаблонного метода.
У вас получается то же самое, что и описано в моей функции, но вызывающееся для структуры у которой переопределён оператор ввода/вывода в поток?
Или же у вас описан шаблонный метод для структур, которые являются объединением с массивом?
Просто у вас там кратко написано *data, так что я путаюсь в догадках.


to sergek:

Ваш способ и есть тот камень преткновения. При изменении структуры, придётся переписывать функции send и receive.

Или я не уловил вашу систему и у вас сериализуются типы по их текстовому названию и вызывается соответствующий сериализатор?


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 06, 2013, 21:11
От увлекающихся обобщенным программированием я ожидал чего-то типа такого
Боюсь вызвать бурю негодования по поводу буста, но! :)
Сейчас я не предлагаю использовать его сериализацию, просто предлагаю посмотреть как синтаксически это реализовано у них. Лаконичней и без лишних флагов.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 06, 2013, 21:18
Код:
TestRecord::TestRecord(){
    push(boolField,"boolField");
    push(charField,"charField");
    push(shortField,"shortField");
    push(intField,"intField");
    push(longField,"longField");
    push(bigintField,"bigintField");
    push(floatField,"floatField");
    push(doubleField,"doubleField");
    push(stringField,"stringField");
    push(dateField,"dateField",BoaDateTime::DateTime);
 }
Здесь каждое поле идентифицируется строкой - дороговато выходит. Также неясно - ну а что дальше что, после того как хапихнули в контейнер (мапу?)

В любом случае в этой функции у вас будет сформированы две функции. Вы их просто сливаете с помощью указания флага. Собственно и предложенная мной функция делает аналогичные действия.
Пример
Код:
void MyStruct::ReadWrite( QDataStream & strm, bool modeWrite )
{
if (modeWrite)
    strm >>  intPoleMyStruct;
    strm >> qstringPoleMyStruct ;
    // здесь еще 100 полей
else
    strm <<  intPoleMyStruct;
    strm << qstringPoleMyStruct ;
    // здесь еще 100 полей
 ...
}
Итого 200 строк. Др вариант
Код
C++ (Qt)
void MyStruct::ReadWrite( QDataStream & strm, int mode )
{
   ReadWrite(strm, &intPoleMyStruct, mode);
   ReadWrite(strm, &qstringPoleMyStruct, mode);
   // здесь еще 100 полей
}
 
Итого 100 строк  :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 06, 2013, 21:20
Да, Igors это гуд, а шаблоны это сила :)



Название: Re: Сериализация (как сделать поудобнее)
Отправлено: sergek от Февраль 06, 2013, 21:26
to sergek:

Ваш способ и есть тот камень преткновения. При изменении структуры, придётся переписывать функции send и receive.

Или я не уловил вашу систему и у вас сериализуются типы по их текстовому названию и вызывается соответствующий сериализатор?
Не уловили ;) При изменении структуры переписывается только конструктор, в котором все сериализуемые поля "сохраняются" методом push. Имена не обязательны, они могут использоваться для доступа к полям путем разыменования (полезно при работе с таблицами БД). А так можно написать:
Код:
    push(boolField);
    push(charField);
    push(shortField);
...
И доступ к полям - обычным образом, и на клиенте и на сервере:
Код:
TestRecord rec;
char ch = rec.charField;
rec.shortField = 123;
Т.е. это обычные классы, только порождены от BoaInterface, в котором все и скрыто (и сериализация, и десериализация, и шифрование и много чего..). Кстати, на основе шаблонов, так не любимых вами ;)
Главное, чтобы объявления класса и реализация конструктора на клиенте и сервере были одинаковы. Поэтому в моих проектах правило: объявления классов, функций и реализации классов - это общая часть серверной и клиентской части.
Реализации функций - разные.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 06, 2013, 21:26
Боюсь вызвать бурю негодования по поводу буста, но! :)
Сейчас я не предлагаю использовать его сериализацию, просто предлагаю посмотреть как синтаксически это реализовано у них. Лаконичней и без лишних флагов.
Та почти так же как я предложил :) Но где ж я в Qt возьму Archive который даст мне автоматычно разрулить read/write  ???


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 06, 2013, 21:31
Но где ж я в Qt возьму Archive который даст мне автоматычно разрулить read/write  ???
Дайте подумать. Напишете сами? :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: sergek от Февраль 06, 2013, 21:33
Код:
TestRecord::TestRecord(){
    push(boolField,"boolField");
    push(charField,"charField");
    push(shortField,"shortField");
    push(intField,"intField");
    push(longField,"longField");
    push(bigintField,"bigintField");
    push(floatField,"floatField");
    push(doubleField,"doubleField");
    push(stringField,"stringField");
    push(dateField,"dateField",BoaDateTime::DateTime);
 }
Здесь каждое поле идентифицируется строкой - дороговато выходит. Также неясно - ну а что дальше что, после того как хапихнули в контейнер (мапу?)
Да, неудачный пример я привел... Мап тут ни при чем, там шаблоны. И каждая строка (push) передает указатель на член класса, а шаблон уже сам определяет тип и длину данных, передаваемых через сокет. Поэтому накладные расходы - один push на один член, извините, данное класса. Примерно так.
Думаю, альтернатива этому - только препроцессор, а это тема отдельного разговора...


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 06, 2013, 21:34
Дайте подумать. Напишете сами? :)
Правда не знаю, я с дустом не очень - так, приобщаюсь к культуре, но не более того  :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 06, 2013, 21:39
там шаблоны. И каждая строка (push) передает указатель на член класса, а шаблон уже сам определяет тип и длину данных, передаваемых через сокет. Поэтому накладные расходы - один push на один член, извините, данное класса. Примерно так.
То есть как бы "контейнер дескрипторов" который разберется с I/O? Смещение члена+тип (+ может что-то еще). Хммм.. а что делать если данные IO не соответствуют полям напрямую? (так бывает)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 06, 2013, 21:43
Правда не знаю, я с дустом не очень - так, приобщаюсь к культуре, но не более того  :)
Буст вообще не причем. Нужны два класса (хотя если захотеть, то хватит и одного): oarchive/iarchive, а внутри у них пусть будет QDataStream.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: sergek от Февраль 06, 2013, 21:47
Давайте так - поставьте конкретную задачу, типа hello, wold (можно сложнее). А я сделаю проект под qt для клиент/серверного приложения. Там и оцените затраты и размер писанного кода. Только не сегодня (результат), а то завтра вставать рано...


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 06, 2013, 21:51
Буст вообще не причем. Нужны два класса (хотя если захотеть, то хватит и одного): oarchive/iarchive, а внутри у них пусть будет QDataStream.
То есть так (создавая класс-прослойку) "лаконичнее" - напустились на бедный флажок :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 06, 2013, 21:52
Давайте так - поставьте конкретную задачу, типа hello, wold (можно сложнее). А я сделаю проект под qt для клиент/серверного приложения. Там и оцените затраты и размер писанного кода. Только не сегодня (результат), а то завтра вставать рано...
Я не прошу реализовывать, вопрос: как пушатся сложные объекты, например, коллекция объектов?


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 06, 2013, 21:54
То есть так (создавая класс-прослойку) "лаконичнее" - напустились на бедный флажок :)
Так вам же его копипастить для сериализации 100500 полей сложных структур.
[off]Убивал бы за такие структуры. :)[/off]

Плюс, в дальнейшем архивы легко расширяются: хочешь в xml, хочешь в json.... :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: alexis031182 от Февраль 06, 2013, 22:06
Может всё-таки просто переопределять операторы << и >> (или вообще любые другие) у каждого класса/структуры, потребных к сериализации, а для вызова сериализации использовать шаблонный класс-контейнер. Если же нужны и простые типы, то просто специализации соответствующие добавить к этому классу. Так оно будет лучше, на мой взгляд. Во-первых, никто лучше не знает как сериализировать данные, нежели как тот объект класса, что ими владеет. Ну а во-вторых, контейнер - просто как единая точка входа/выхода получится.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 06, 2013, 22:17
Может всё-таки просто переопределять операторы << и >> (или вообще любые другие) у каждого класса/структуры, потребных к сериализации, а для вызова сериализации использовать шаблонный класс-контейнер. Если же нужны и простые типы, то просто специализации соответствующие добавить к этому классу. Так оно будет лучше, на мой взгляд. Во-первых, никто лучше не знает как сериализировать данные, нежели как тот объект класса, что ими владеет. Ну а во-вторых, контейнер - просто как единая точка входа/выхода получится.
Разговор немного о другом. Сериализацией и так будет заниматься сам объект, а вот вместо двух (часто идентичных) операторов << >>, будет один.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: sergek от Февраль 06, 2013, 22:17
Я не прошу реализовывать, вопрос: как пушатся сложные объекты, например, коллекция объектов?
Для этого есть параметризованный класс, аналог реляционной таблицы. Например, передача таблицы User_inf описывается так:
Код:
// запись для просмотра списка пользователей
class Ruser_inf : public BoaInterface
{
public:
    Ruser_inf();
    int id_user;
    char user_type;
    bool deny;
    string user_name;
    string firstname;
    string lastname;
    string patronymic;
    string organization;
};
typedef BoaTable<Ruser_inf> User_inf;
BoaDataSet* selUser_info(Bint* sortOrder);
А пушатся члены-данные записи. Вообще, библиотека заточена под реляционную БД, т.е прямоугольные однородные таблицы.
Ну, наверное, можно придумать пример, который не впишется в мою схему. Но хотелось бы конкретики, желательно, из жизни. А вдруг, решаемо...


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 06, 2013, 22:28
А пушатся члены-данные записи. Вообще, библиотека заточена под реляционную БД, т.е прямоугольные однородные таблицы.
Т.е. есть свой класс коллекция, которая умеет себя сериализовать.

Ну, наверное, можно придумать пример, который не впишется в мою схему. Но хотелось бы конкретики, желательно, из жизни. А вдруг, решаемо...
Есть любой std::map. Как его сериализовать таблицей, а главное как потом десериализовать?


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 06, 2013, 22:31
От увлекающихся обобщенным программированием я ожидал чего-то типа такого
Код
C++ (Qt)
template <class Stream, class T>
void ReadWrite( Stream & strm, T * data, int mode )
{
switch (mode)
...
 
void MyStruct::ReadWrite( QDataStream & strm, int mode )
{
   ReadWrite(strm, &intPoleMyStruct, mode);
   ...
 
Для определенных задач, когда клиент и сервер обменивается одинаковыми структурами в обе стороны, такой вариант может облегчить жизнь. Для других задач надо иметь в виду такой момент: если клиент отправляет серверу одну структуру типа request, а сервер в ответ другую типа response (и наоборот не будет), то при таком подходе этот вариант не самый лучший. Да, он в два раза уменьшит исходный код сериализации, но и в два раза увеличит конечный (скомпилированный) :). Потому что клиенту не нужен read(request) и write(response), а серверу не нужен read(response) и write(request). В этом же варианте при любом значении mode скомпилируется код на чтение и на запись сразу, хоть это и не всем нужно.

Вариант sergek, конечно, хорош, но общий предок - это не совсем честно :). С общим предком много чего можно сделать интересного. Можно взять общего предка QObject и практически бед не знать с его metaObject. Правда туда стороннюю произвольную структуру не воткнешь, и вернемся к тому, с чего начали.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 06, 2013, 22:33
Для определенных задач, когда клиент и сервер обменивается одинаковыми структурами в обе стороны, такой вариант может облегчить жизнь.
Что то мне подсказывает, что топикстартеру нужна сериализация только на диск. ;)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: alexis031182 от Февраль 06, 2013, 22:35
Разговор немного о другом. Сериализацией и так будет заниматься сам объект, а вот вместо двух (часто идентичных) операторов << >>, будет один.
Вариант Igors понятен. Если не сложно, уточните, что делается в буст?


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: alexis031182 от Февраль 06, 2013, 22:36
Что то мне подсказывает, что топикстартеру нужна сериализация только на диск. ;)
Эта тема - продолжение темы про сериализацию данных, передаваемых через сеть.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: alexis031182 от Февраль 06, 2013, 22:38
Для определенных задач, когда клиент и сервер обменивается одинаковыми структурами в обе стороны, такой вариант может облегчить жизнь. Для других задач надо иметь в виду такой момент: если клиент отправляет серверу одну структуру типа request, а сервер в ответ другую типа response (и наоборот не будет), то при таком подходе этот вариант не самый лучший. Да, он в два раза уменьшит исходный код сериализации, но и в два раза увеличит конечный (скомпилированный) :). Потому что клиенту не нужен read(request) и write(response), а серверу не нужен read(response) и write(request). В этом же варианте при любом значении mode скомпилируется код на чтение и на запись сразу, хоть это и не всем нужно.
В бинарник попадёт только то, что используется.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 06, 2013, 22:40
Если не сложно, уточните, что делается в буст?
Вот ссылка на примеры: http://www.boost.org/doc/libs/1_53_0/libs/serialization/doc/index.html


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: alexis031182 от Февраль 06, 2013, 22:48
Вот ссылка на примеры: http://www.boost.org/doc/libs/1_53_0/libs/serialization/doc/index.html
Спасибо. Классное решение!


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 06, 2013, 22:52
В бинарник попадёт только то, что используется.
Насколько я понимаю, в этом случае как раз и используется и попадет, т.к. в одном методе находится, и туда развернется шаблон и на чтение и на запись. Есть и другие особенности: виртуальный метод или экспортируется в библиотеке или еще чего (навскидку не припомню). Так что есть случаи, когда вроде бы неиспользуемое может в бинарник попасть.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: alexis031182 от Февраль 06, 2013, 23:03
Насколько я понимаю, в этом случае как раз и используется и попадет, т.к. в одном методе находится, и туда развернется шаблон и на чтение и на запись. Есть и другие особенности: виртуальный метод или экспортируется в библиотеке или еще чего (навскидку не припомню). Так что есть случаи, когда вроде бы неиспользуемое может в бинарник попасть.
Специализации шаблона функции можно просто вставить, где вместо аргумента Mode этот Mode будет типом, и тогда будет использоваться то, что нужно. Но при этом код будет един для клиента и сервера. А вообще вот в бусте отличная идея заложена на это дело. Лаконичнее наверное не придумаешь.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 07, 2013, 00:09
Специализации шаблона функции можно просто вставить, где вместо аргумента Mode этот Mode будет типом, и тогда будет использоваться то, что нужно. Но при этом код будет един для клиента и сервера.
Да, можно Mode сделать параметром шаблона, а не функции (тогда шаблоны Igors будут немного по другому выглядеть). При этом Mode должен быть известен в compile-time, что накладывает некоторые ограничения. В частности смотреть, что будет при наследовании, полях-ссылках на другие структуры и т.п.

А вообще вот в бусте отличная идея заложена на это дело. Лаконичнее наверное не придумаешь.
Способ буста хорош, тут не поспоришь. Но это надо буст с собой носить, не всем это нравится. Опять же, должна быть возможность для модификации исходных структур, а если такой возможности нет? Способ QDataStream в этом отношении мне как-то ближе :), но нет доступа к защищенным/приватным членам :D. И как тут не разорваться...  ???.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: alexis031182 от Февраль 07, 2013, 00:48
Да, можно Mode сделать параметром шаблона, а не функции (тогда шаблоны Igors будут немного по другому выглядеть).
Я и имел в виду параметром шаблона функции (специализацию шаблона функции, а не класса). Впрочем, конечно можно и класс привязать.

При этом Mode должен быть известен в compile-time, что накладывает некоторые ограничения.
Да, в compile-time, причём легко число можно сделать типом и тогда пойдёт совсем другая пляска. Не вижу ограничений, просто иной подход.

В частности смотреть, что будет при наследовании, полях-ссылках на другие структуры и т.п.
Это если класс. Но и это обходится через абстрактный интерфейс.

Я лично всегда за compile-time. Меня меньше всего волнует размер бинарника, т.к. он всегда в моём случае даже и близко несопоставим с размерами подключаемых библиотек, какие бы я шаблоны и в каком количестве не использовал. А вот преимущества от compile-time по моему очевидны. Впрочем, дело вкуса, т.к. отладка шаблонов, она, мягко говоря, кошмарна.

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

Способ QDataStream в этом отношении мне как-то ближе :), но нет доступа к защищенным/приватным членам :D. И как тут не разорваться...  ???.
Плохо, когда нет системы в коде. Нужно обязательно её создать, иначе будет как минимум беспорядок. Поэтому, если для поддержки принятого в системе подхода требуется чем-то пожертвовать, даже собственными принципами, то лучше это сделать как можно раньше. А вообще конечно, самое трудное - это выбрать систему.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 07, 2013, 01:30
Вы когда закончите оценивать различные шаблонные, для меня непонятные технологии, предоставьте лучший вариант :)

А ещё желательно с примерами в коде :D

PS заложите, так сказать, правильный подход к сериализации. А то у меня сериализация мозголомная в проекте, вон я тему создавал - наследие драконов прям :D


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: alexis031182 от Февраль 07, 2013, 01:59
Жаль частичная специализация для функций не поддерживается. Так то наверное правильнее называть специализацию для функций перегрузкой, потому как она в принципе и получается, но всё равно, тоже работает:
Код
C++ (Qt)
template<class Stream, class T>
void ReadWrite(Stream &strm, T *data);
 
template<class Stream, class T, int>
void ReadWrite(Stream &strm, T *data) {
   qDebug() << 1;
}
 
template<class Stream, class T, int, int>
void ReadWrite(Stream &strm, T *data) {
   qDebug() << 2;
}

И собственно вызов:
Код
C++ (Qt)
int strm, data;
ReadWrite<int, int, 0>(strm, &data);
ReadWrite<int, int, 0, 0>(strm, &data);


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: alexis031182 от Февраль 07, 2013, 02:04
Вы когда закончите оценивать различные шаблонные, для меня непонятные технологии, предоставьте лучший вариант :)

А ещё желательно с примерами в коде :D

PS заложите, так сказать, правильный подход к сериализации. А то у меня сериализация мозголомная в проекте, вон я тему создавал - наследие драконов прям :D
Я бы выбрал бустовский вариант. Буст сам тащить ни к чему, там в примерах вся идея с одним оператором и так показана, можно в собственном классе-шаблоне повторить.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: sergek от Февраль 07, 2013, 08:57
Ну, наверное, можно придумать пример, который не впишется в мою схему. Но хотелось бы конкретики, желательно, из жизни. А вдруг, решаемо...
Есть любой std::map. Как его сериализовать таблицей, а главное как потом десериализовать?

Если в простом случае, когда ключ и значение сводится к классам с членами-данными стандартных типов (map<T1,T2>), то легко - две таблицы, одна передает T1, вторая T2. На приемной стороне ассоциативный массив "слепляется" из двух половинок.
В общем случае, нужна декомпозиция данных, приведение к стандартным типам и в цикле передача составных частей. На приемной стороне - обратное восстановление. Но в этом случае изменение структуры данных, параметризующих map, приведет к изменению и клиентской и серверной функции.
Я поэтому и говорил о конкретном примере, чтобы можно было оценить ограничение метода.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 07, 2013, 11:30
PS заложите, так сказать, правильный подход к сериализации. А то у меня сериализация мозголомная в проекте, вон я тему создавал - наследие драконов прям :D

Так это смотря что сериализовать, как и куда :).

1. Если много простых, несвязанных между собой структур, да большинство из которых нельзя модифицировать, то надо смотреть в сторону способа как в QDataStream.
2. Все структуры ваши и можете делать с ними все, что угодно, то брать буст или сделать аналогично.
3. Как п. 2, структуры разрозненные, но есть возможность добавить им всем общего предка, то сравнить способ буста и способ sergek.
4. Классы хорошо организованы в иерархии, довольно громоздкие, в некоторых случаях требуют индивидуальной обработки полей и есть возможность добавить общего предка, то можно попробовать слепить такого странного монстра XMLContentReader (https://www.assembla.com/code/book_store_example/git/nodes/master/Modules/BookStore/include/BookStore/IO/XMLContentReader.h)/XMLContentWriter (https://www.assembla.com/code/book_store_example/git/nodes/master/Modules/BookStore/include/BookStore/IO/XMLContentWriter.h).

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

А с шаблонами лучше разобраться, штука очень мощная, может сильно уменьшить объем написания кода. Правда мозги при этом свернутся :D.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: panAlexey от Февраль 07, 2013, 13:59
to sergek:
Ваш способ и есть тот камень преткновения. При изменении структуры, придётся переписывать функции send и receive.
Или я не уловил вашу систему и у вас сериализуются типы по их текстовому названию и вызывается соответствующий сериализатор?
Много ли у вас таких структур, что надо волноваться?
В моем проекте (http://code.google.com/p/unnstudioreport/) приходится поддерживать насколько форматов. Структуры там разные, их немало.
Проблемы конечно есть, но решил, что аккуратность будет от них лекарством.

При проектировании сериализации воспользовался механизмом наследования от абстрактного класса и патерном "посетитель":
Абстрактный класс спасает от "забывчивости", а посетитель позволяет сделать 1 раз обход документа разными сериализаторами, а детали сериализации скрыты в конкретных сериализаторах в нужных функциях. Я их писал рядышком парами: сохранение / считывание. Это позволяет визуально контролировать правильность.
Код:
/// Сохранение отчета. Инициализируем лоадер, вызываем сохранение...
bool uoReportDoc::saveToFile(QString path, uoRptStoreFormat stFormat, uoReportSelection* sel)
{
bool retVal = false;
if (stFormat == uoRsf_Unknown) {
qWarning() << tr("no define store format");
return false;
}
uoReportLoader* loader = uoReportLoader::getLoader(stFormat);
if (!loader) {
qWarning() << tr("Can not create loader"); return false;
}

loader->setFileName(path);
if (!loader->initFO(false)){
qWarning() << loader->getLastError();
} else {
flush(loader, sel);
}
loader->finalize();
delete loader;

return retVal;
}


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 07, 2013, 14:01
По поводу охов и ахов насчет буста
Код
C++ (Qt)
ar & g.degrees;
Значит вот это круто. А что бы Вы сказали если бы это написал велосипедист (который наверное ничего не знает)? Да Вы бы его охаяли с ног до головы :) "Это что, интуититвный оператор & ? Да откуда я могу догадаться что он делает??". НО.. это великий дуст - и лояльность меняется на 180 градусов. "Ах как лаконично"  :)

И собственно вызов:
Код
C++ (Qt)
int strm, data;
ReadWrite<int, int, 0>(strm, &data);
ReadWrite<int, int, 0, 0>(strm, &data);
Ну понеслась template-вакханалия  :) Надо задрочить текст ну до полной невменяемости! Число typedef должно быть (намного) больше собственно кода. Это ж круто, вышак, типа "для избранных". А еще в этой пучине легко спрятать по существу слабенький функционал - кто там разберется за лесом шаблонов?
(Александр, мое мнение субъективно  :))

sergek, спасибо за предложение тестового примера, но тут "эффект большого" - на простом hello world вероятно любая схема успешно отработает. Если нетрудно покажите (или расскажите) что делает метод push. Из Вашего кода я не понял как же (используя что) разруливается I/O. Спасибо


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 07, 2013, 14:05
Примерно десяток используемых сейчас и сотенки две на подходе. :)

Это крупный проект, который почти загнулся от старости и поверх него кладут-кладут-кладут новые и новые кирпичики :)

to Igors:
Я люблю понятливый код. Когда я в первый раз увидел бустовский функтор с сериализацией типов и полез внутрь я ошалел :) Примерно десятка три макросов, шаблонов дофига и всё тяяяяяянется.

А используя <<  получаем наглядность, простоту (ну да, жертвуем короткой строкой, получаем сразу видимый для другого человека результат) и использование уже готовых гадостей решений.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 07, 2013, 14:15
А используя <<  получаем наглядность, простоту (ну да, жертвуем короткой строкой, получаем сразу видимый для другого человека результат) и использование уже готовых гадостей решений.
Если устраивают просто операторы << и >>, то лучше пока ничего и не изобретать, решение должно идти "от жизни". 


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 07, 2013, 14:39
По поводу охов и ахов насчет буста
Код
C++ (Qt)
ar & g.degrees;
Значит вот это круто.
Не это не круто. Круто было бы так:
Код
C++ (Qt)
ar <> g.degrees;
 
или так:
Код
C++ (Qt)
ar <<>> g.degrees;
 
:)

А что бы Вы сказали если бы это написал велосипедист (который наверное ничего не знает)? Да Вы бы его охаяли с ног до головы :)
Здесь буст прозвучал, только что бы показать, что можно совсем без доп. флагов, точнее архив сам этот флаг знает.
Если бы вы предложили решение без ненужного флага, то про буст вряд ли бы вспомнили. ;)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 07, 2013, 14:43
По поводу охов и ахов насчет буста
Код
C++ (Qt)
ar & g.degrees;
Значит вот это круто. А что бы Вы сказали если бы это написал велосипедист (который наверное ничего не знает)? Да Вы бы его охаяли с ног до головы :) "Это что, интуититвный оператор & ? Да откуда я могу догадаться что он делает??". НО.. это великий дуст - и лояльность меняется на 180 градусов. "Ах как лаконично"  :)
Не все в восторге от буста, но можно смотреть в таком разрезе, что люди там таки довольно много думали над проблемами и нашли не самые плохие способы их решения. А зачастую и самые лучшие в контексте поставленной задачи. Оператор &, шаблоны и макросы там не от хорошей жизни используют. Кстати, из-за обилия макросов мне этот буст и не нравится. Но Вы, когда будете развивать свой метод по объединению чтения/записи, верите или нет, наверняка придете к тому, что сейчас есть в бусте :). А сейчас там есть определение перечня полей, подлежащих сериализации. И ничего лишнего в самих структурах. Всем остальным занимаются отдельные сериализаторы, с помощью которых хоть в QDataStream, хоть в XML, хоть куда можно сохранять. Т.е. в структуру добавить что-нить типа:
Код
C
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
 ar & degrees;
 ar & minutes;
 ar & seconds;
}
и не надо больше никаких методов по вводу/выводу писать в каждой структуре. Вы же к этому и стремились: один раз описать поля и больше с ними не возиться :).

Это, опять же, не призыв этим способом пользоваться, и не возведение в абсолют буста, но как метод сериализации очень достоин внимания. Вы вполне можете сделать свой велосипед, который будет красивше и понятнее :).


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 07, 2013, 14:48
Примерно десяток используемых сейчас и сотенки две на подходе. :)

Это крупный проект, который почти загнулся от старости и поверх него кладут-кладут-кладут новые и новые кирпичики :)

Тут вопрос такой: есть ли наследование в этих структурах, и поля-ссылки на другие структуры, в частности ссылка на базовый тип, когда фактически там может быть и производный объект?


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 07, 2013, 14:59
Все структуры взаимосвязаны.
Фактически они являются нодами огромнейшей системы. Каждая нода может ссылаться на данные, на другую ноду, быть объединённой с другой нодой. Имеются ноды различных типов, начиная от физ устройства, заканчивая виртуальными узлами. Собственно получается этакий чоООООрный круг. Что на самом деле не круг, а граф связей между ними :D

Да, ссылок может быть несколько :D


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: alexis031182 от Февраль 07, 2013, 15:08
По поводу охов и ахов насчет буста
Код
C++ (Qt)
ar & g.degrees;
Значит вот это круто. А что бы Вы сказали если бы это написал велосипедист (который наверное ничего не знает)?
Я бы его сразу зауважал :)

Да Вы бы его охаяли с ног до головы :) "Это что, интуититвный оператор & ? Да откуда я могу догадаться что он делает??". НО.. это великий дуст - и лояльность меняется на 180 градусов. "Ах как лаконично"  :)
Хаять бы уж точно не стал. А вот то, что решение у буста весьма и весьма, тут вряд ли Вы станете спорить. Что Вам там интуитивно непонятно?

И собственно вызов:
Код
C++ (Qt)
int strm, data;
ReadWrite<int, int, 0>(strm, &data);
ReadWrite<int, int, 0, 0>(strm, &data);
Ну понеслась template-вакханалия  :)
Ну раз речь зашла о шаблонах, так оно и вот... :)

Надо задрочить текст ну до полной невменяемости! Число typedef должно быть (намного) больше собственно кода. Это ж круто, вышак, типа "для избранных". А еще в этой пучине легко спрятать по существу слабенький функционал - кто там разберется за лесом шаблонов?
(Александр, мое мнение субъективно  :))
Конечно субъективно :) Непонятно только, что тут вызывает у Вас такой антагонизм. Ну шаблоны, ну typedef, ну не пользуйтесь, раз не нравится. Как бы свободу выбора, хотя бы в этом, пока ещё не отменяли :)

А на самом деле, код на шаблонах очень красив, если делается с чувством, толком, расстановкой. Да, порог вхождения для понимания выше, но это же не значит, что это сразу плохо. И никто здесь грудь не выпячивает, мол, "я всех порвал, я на шаблонах прогаю". Просто идёт обсуждение различных подходов к проблеме сериализации. Вот и всё :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 07, 2013, 15:25
Если бы вы предложили решение без ненужного флага, то про буст вряд ли бы вспомнили. ;)
Почему ненужного? Возможно полезно явно видеть какая операция. Кстати а возможны ли др режимы кроме read/write? Может и да. Также мне лично куда приятнее имя ReadWrite чем абстрактный &.  К сожалению, обычно человеку "который много знает" копаться в этом неинтересно. "Там правильно" - значит все что "не так как там" - неправильно  :'(

и не надо больше никаких методов по вводу/выводу писать в каждой структуре. Вы же к этому и стремились: один раз описать поля и больше с ними не возиться :).
Так почему бы не сказать проще "используй сериализацию буста, там давно за тебя все сделали". Однако не все спешат это делать - хотя сериализация есть у каждого. Был слушок что там проблемы с версиями - правда никаких подробностей не знаю. Да и просто перелопатить с Archive множество живых классов как-то не тянет. Да, в Qt кое-чего нет, так что, сразу привлекать др либу? Может и свои решения будут неплохи (или такая мысль не допускается?  :))


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 07, 2013, 15:44
Почему ненужного? Возможно полезно явно видеть какая операция.
Вы определитесь. То вы яростный противник копипаста, то большой сторонник. :)
Передавать его в 100500 функций нет никакой необходимости, а если он понадобится, то его всегда можно спросить у архива.

К сожалению, обычно человеку "который много знает" копаться в этом неинтересно. "Там правильно" - значит все что "не так как там" - неправильно  :'(
К сожалению, не бывает так в программировании: там правильно, значит там не правильно. :(
А есть постоянный выбор, это удобней, это быстрей, это места меньше занимает, а это кроссплатформенно и много много такого.

Так почему бы не сказать проще "используй сериализацию буста, там давно за тебя все сделали".
Я почти во всех своих постах вынужден писать, что не нужно использовать сериализацию буста, а вот посмотреть удобный синтаксис и сделать так же у себя можно. Это просто. Захотите, что бы внутри был QDataStream - пожалуйста, захотите QDomDocument - да легко.
Страшно, что то предложить, все такие чувствительные. Что за комплексы? :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 07, 2013, 15:58
Все структуры взаимосвязаны.
Фактически они являются нодами огромнейшей системы. Каждая нода может ссылаться на данные, на другую ноду, быть объединённой с другой нодой. Имеются ноды различных типов, начиная от физ устройства, заканчивая виртуальными узлами. Собственно получается этакий чоООООрный круг. Что на самом деле не круг, а граф связей между ними :D

Да, ссылок может быть несколько :D

Если коротко, то способ с переопределением операторов для QDataStream не будет работать для полиморфных классов. Например:
Код
C++ (Qt)
class A {};
class B : public A {};
 
QDataStream & operator<< (QDataStream& stream, const A& a);
QDataStream & operator>> (QDataStream& stream, A& a);
 
QDataStream & operator<< (QDataStream& stream, const B& b);
QDataStream & operator>> (QDataStream& stream, B& b);
 
main
{
 A* b = new B();
 
QDataStream out(&file);
out << *b; // Call QDataStream & operator<< (QDataStream& stream, A& a);
}
будет записана только часть класса А, поля В сохранены не будут. Потому что используемый шаблон выбирается в compile-time, и компилятор не знает, какой фактически объект будет в run-time. Если у вас есть дочерние ноды, они создаются динамически и в compile-time их конкретный тип не известен, то Вас ожидают такие вот грабли :). И есть еще много других :D.

В способе буста тоже не все так безоблачно, стоить посмотреть Pointers to Objects of Derived Classes (http://www.boost.org/doc/libs/1_53_0/libs/serialization/doc/serialization.html#derivedpointers). Я о таком подозревал, и когда увидел это, стало еще тоскливее...


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 07, 2013, 16:09
Так почему бы не сказать проще "используй сериализацию буста, там давно за тебя все сделали". Однако не все спешат это делать - хотя сериализация есть у каждого. Был слушок что там проблемы с версиями - правда никаких подробностей не знаю. Да и просто перелопатить с Archive множество живых классов как-то не тянет. Да, в Qt кое-чего нет, так что, сразу привлекать др либу? Может и свои решения будут неплохи (или такая мысль не допускается?  :))
Выше уже сказали, что никто не заставляет использовать, а посмотреть на реализацию вполне стоит :).

Если бы буст был так хорош, и его бы использовали в Qt, я бы тоже им пользовался безоговорочно. Но разработчики Qt не используют его, более того, изобретают свои велосипеды, взять хоты бы их контейнеры QVector, QList и т.п. Хотя, казалось бы, бери хоть из std, хоть из буста. Велосипеды Qt мне нравятся намного больше, чем другие. Поэтому я пользуюсь Qt и добавляю туда свои запчасти, и не использую буст. Но это не значит, что нельзя в бусте подглядывать интересные и полезные моменты :).


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 07, 2013, 16:10
Как бы это ни было, но оно есть и сериализация там монструозная. Причём даже дерева не построить - будут конфликты :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 07, 2013, 16:18
Если бы буст был так хорош, и его бы использовали в Qt,
Ну в обще-то буст очень хорошо, очень-очень. А не используют его в Qt совсем по другим причинам. Велосипеды в Qt свои, потому что в те времена, когда его начинали разрабатывать не было даже нормальной поддержки строк. Плюс Тролли хотели полной автономности и лишние зависимости были просто не нужны.

И да, если когда нибудь, не дай Бог конечно, вам придется писать софт без Qt обратите внимание на буст. Он многое предоставляет.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 07, 2013, 16:31
Плюс Тролли хотели полной автономности и лишние зависимости были просто не нужны.
У меня примерно такие же аргументы были. И плюс, например, по контейнерам надо было выбирать. Все-таки чем-то одним надо пользоваться.

И да, если когда нибудь, не дай Бог конечно, вам придется писать софт без Qt обратите внимание на буст. Он многое предоставляет.
Смотрел туда одно время... На довольно низком уровне, для кроссплатформенности, похоже лучше и не найдешь библиотеки. Но хотелось уровень повыше и удобства, поэтому победил Qt :D. И буст таскать с собой не захотелось.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 07, 2013, 17:00
И буст таскать с собой не захотелось.
Большинство бустовских библиотек просто разворачиваются в коде, т.е. таскать с бинарником разделяемые библиотеки не нужно. К тому же его всегда можно собрать статически.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 07, 2013, 17:21
А как насчет того чтобы вернуться к теме? :)  Я согласен со всем что сказали ViTech и Old, но согласитесь, конструктивных решений это никак не прибавляет. Что мы имеем? Ну мой (сопливый) вариант, систему sergek (которой пока не понял) и... что еще?

- ничего не делать, перетерпеть как-нибудь (ручками)
- ах как хорош буст, но ведь все имеет свои стороны (и пошла философия)
- мутные предложения сделать какой-то оператор (хз какой  ???) и цитирование Вереса (в начале темы)

Негусто. Или я что-то упустил?


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 07, 2013, 17:34
Плохо читаете :)

1) Сериализация с использованием шаблонов (Igors)
2) Строковые идентификаторы для каждой переменной (sergeich)
3) Объединение включающее структуру и массив байт (данные всегда находятся в виде массива байтов, ничего сериализовать и десериализовывать не надо) (Bepec)
4) Переопределение операторов << и >> с простейшим упихиванием всего в QDataStream  (Bepec)
5) Использование готовых библиотек сериализации (boost, s11n). Правда описывать сериализатор и десериализатор тоже нужно :)
6) Написание своего сериализатора и десериализатора для каждого объекта. На выходе получаем стандартный контейнер (alexis)
7) Написание отдельных сериализаторов для каждого алгоритма сериализации (XML) и подключение их по мере необходимости (ViTech)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 07, 2013, 17:46
3) Объединение включающее структуру и массив байт (данные всегда находятся в виде массива байтов, ничего сериализовать и десериализовывать не надо) (Bepec)
От этого меня отучили еще когда понадобилось гонять данные между  PPC и Intel. Причем понимание что так делать плохо приходит быстро и навсегда :) По поводу остального
Цитировать
Вы сказали много интересного и нового. Но интересное не ново, а новое не интересно



 


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: alexis031182 от Февраль 07, 2013, 18:16
Получается, Верес победил. У него больше всего вариантов :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 07, 2013, 18:24
В любом случае всё разбивается на три кучки:

В первой простота и понятность кода. Т.е. будет ясно каждому более-менее знакомому с языком, можно поправить без особых усилий.
Цитировать
3) Объединение включающее структуру и массив байт (данные всегда находятся в виде массива байтов, ничего сериализовать и десериализовывать не надо) (Bepec)
4) Переопределение операторов << и >> с простейшим упихиванием всего в QDataStream  (Bepec)
6) Написание своего сериализатора и десериализатора для каждого объекта. На выходе получаем стандартный контейнер (alexis)

Во второй использование сторонних библиотек - там всё продумали за нас, вот только не гарантируют что новая версия библиотек будет поддерживать старые функции и типы сериализации (буст тому подтверждение)
Цитировать
5) Использование готовых библиотек сериализации (boost, s11n). Правда описывать сериализатор и десериализатор тоже нужно Улыбающийся

В третьей группе у нас находится написание собственных сериализаторов с использованием шаблонов, отдельных файлов заголовков для сериализации, строковых идентификаторов.
Пользоваться и создавать можно, разбор и правка может вызвать затруднения
Цитировать
1) Сериализация с использованием шаблонов (Igors)
2) Строковые идентификаторы для каждой переменной (sergeich)
7) Написание отдельных сериализаторов для каждого алгоритма сериализации (XML) и подключение их по мере необходимости (ViTech)

У меня только два мнения :)
Первое - это метод Igors (т.к. шаблоны действительно сильно упрощают код, но я ими пока слабо работаю, так что на будущее).
Второе - первая группа из-за простоты её поддержки/правки/добавления нового функционала и возможности автоматизирования её создания.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 07, 2013, 18:26
Негусто. Или я что-то упустил?

Есть одно маленькое упущение: чтобы получить конструктивное решение, надо описать конструктивное задание :). Потому как нет одного универсального метода, который ко всем случаям оптимально подойдет.

Поэтому повторюсь:
Цитировать
Так это смотря что сериализовать, как и куда. (http://www.prog.org.ru/index.php?topic=24127.msg171271#msg171271)
...есть ли наследование в этих структурах и т.п.... (http://www.prog.org.ru/index.php?topic=24127.msg171290#msg171290)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 07, 2013, 18:32
Негусто. Или я что-то упустил?
Хорошо, давайте добавим немного кода:
Код
C++ (Qt)
#ifndef serialize_h
#define serialize_h
 
#include <QDataStream>
 
class archive
{
public:
enum rw_mode { read, write };
 
virtual ~archive() {};
virtual rw_mode mode() const = 0;
};
 
class iarchive : public archive
{
public:
rw_mode mode() const { return read; }
};
 
class oarchive : public archive
{
public:
rw_mode mode() const { return write; }
};
 
class qds_iarchive : public iarchive
{
public:
qds_iarchive( QIODevice *dev ) : m_stream( dev ) {}
 
template<typename T>
qds_iarchive &operator&( T &data )
{
m_stream >> data;
return *this;
}
 
private:
QDataStream m_stream;
};
 
class qds_oarchive : public iarchive
{
public:
qds_oarchive( QIODevice *dev ) : m_stream( dev ) {}
 
template<typename T>
qds_oarchive &operator&( const T &data )
{
m_stream << data;
return *this;
}
 
private:
QDataStream m_stream;
};
 
#endif // serialize_h
 

Код
C++ (Qt)
#include <QCoreApplication>
#include <QFile>
#include <QDebug>
#include "serialize.h"
 
int main( int argc, char *argv[] )
{
QCoreApplication app( argc, argv );
 
{
QFile file( "test.dat" );
if( !file.open( QIODevice::WriteOnly ) )
{
qWarning() << "File open error (write)";
return 1;
}
 
qint8  a = 0x10;
qint16 b = 0x20;
qint32 c = 0x30;
 
qds_oarchive os( &file );
os & a;
os & b;
os & c;
}
 
{
QFile file( "test.dat" );
if( !file.open( QIODevice::ReadOnly ) )
{
qWarning() << "File open error (read)";
return 1;
}
 
qint8  a;
qint16 b;
qint32 c;
 
qds_iarchive is( &file );
is & a;
is & b;
is & c;
 
qDebug() << hex << a << b << c;
}
 
return 0;
}
 


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: panAlexey от Февраль 07, 2013, 20:50
Хорошо, давайте добавим немного кода:
Код
C++ (Qt)
qDebug() << hex << a << b << c;
 
а это что: qDebug() << hex << a << b << c; ??


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 07, 2013, 20:56
а это что: qDebug() << hex << a << b << c; ??
Манипулятор. Для печати в 16-ричной системе.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: sergek от Февраль 07, 2013, 21:22
sergek, спасибо за предложение тестового примера, но тут "эффект большого" - на простом hello world вероятно любая схема успешно отработает. Если нетрудно покажите (или расскажите) что делает метод push. Из Вашего кода я не понял как же (используя что) разруливается I/O. Спасибо

Метод push сохраняет информацию о каждом члене класса (указатель, тип, размер). Разумеется, метод параметризуемый:

Код:
template<class C> void BoaInterface::push(C& ptr,const std::string& fldName,int size){
    bool hasName=!fldName.empty();
    BoaElement* elem;
    // Определяем тип преременной. Обрабатываем только известные типы
    if(typeid(ptr)==typeid(bool))                  elem=new BoolBoaElement(&ptr,hasName);
    else if(typeid(ptr)==typeid(char))             elem=new CharBoaElement(&ptr,hasName);
    else if(typeid(ptr)==typeid(short))            elem=new ShortBoaElement(&ptr,hasName);
    else if(typeid(ptr)==typeid(unsigned short))   elem=new ShortBoaElement(&ptr,hasName);
    else if(typeid(ptr)==typeid(int))              elem=new IntBoaElement(&ptr,hasName);
    else if(typeid(ptr)==typeid(long))             elem=new IntBoaElement(&ptr,hasName);
    else if(typeid(ptr)==typeid(unsigned))         elem=new IntBoaElement(&ptr,hasName);
    else if(typeid(ptr)==typeid(unsigned long))    elem=new IntBoaElement(&ptr,hasName);
    else if(typeid(ptr)==typeid(bigint))           elem=new BigIntBoaElement(&ptr,hasName);
    else if(typeid(ptr)==typeid(float))            elem=new FloatBoaElement(&ptr,hasName);
    else if(typeid(ptr)==typeid(double))           elem=new DoubleBoaElement(&ptr,hasName);
    else if(typeid(ptr)==typeid(std::string))      elem=new StringBoaElement(&ptr,hasName,size);
    else if(typeid(ptr)==typeid(BoaDataSet))       elem=new DataSetBoaElement(&ptr,hasName);
    else if(typeid(ptr)==typeid(BoaBinObject))     elem=new BinaryBoaElement(&ptr,hasName);
    else if(typeid(ptr)==typeid(BoaDateTime))      elem=new DateTimeBoaElement(&ptr,hasName,size);
    else if(typeid(ptr)==typeid(BoaBlob))          elem=new BlobBoaElement(&ptr,hasName);
    else if(strstr(typeid(ptr).name(),"BoaTable")) elem=new TableBoaElement(&ptr,hasName);
    else elem=new UnknownBoaElement(&ptr,hasName);
    data.push_back(elem);
}
Информация эта сохраняется в векторе data:
Код:
    std::vector<BoaElement*> data;      // список элементов данных
который представляет список объектов полиморфных типов (стольких, сколько поддерживаемых типов данных), каждый из которых знает, как себя сохранить в буфер, и как себя восстановить из буфера:
Код:
class BoaElement
{
public:
...
    // скопировать данные объекта в буфер по адресу ptr
    virtual int objToBuf(char* ptr) = 0;
    // скопировать данные из буфера по адресу ptr в объект
    virtual int bufToObj(char* ptr) = 0;
};
Например, для int:

Код:
int IntBoaElement::objToBuf(char* ptr){
    int dataLen=sizeof(int);
    if(ptr) memcpy(ptr,val,dataLen);
    return dataLen;
}
int IntBoaElement::bufToObj(char* ptr){
    int dataLen=sizeof(int);
    memcpy(val,ptr,dataLen);
    return dataLen;
}

Собственно, и все. Когда в конструкторе класса мы пишем
Код:
    push(boolField);
    push(charField);
    push(shortField);
этим самым мы сохраняем информацию о "сериализуемых" данных, а всю технику прячем в методы базового (интерфейсного) класса. Когда надо передать (сериализовать) объект, вызывается интерфейсный метод send(), в котором последовательно перебирается список объектов упомянутых выше полиморфных типов, и для каждого вызывается метод objToBuf(). При восстановлении (приеме) вызывается метод receive(), выполняющий обратную задачу.
Поэтому для того, чтобы сохранить (или передать) объект, а потом его восстановить не надо определять какие-то дополнительные методы. Достаточно:
1) сериализуемый тип породить от интерфейсного класса;
2) написать конструктор, в котором тупо перечислить все его члены-данные вызовом push().
Все остальное (много чего) прозрачно для разработчика и не требует кодирования под каждую задачу.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 07, 2013, 21:24
Непонятно только, что тут вызывает у Вас такой антагонизм. Ну шаблоны, ну typedef, ну не пользуйтесь, раз не нравится. Как бы свободу выбора, хотя бы в этом, пока ещё не отменяли :)
Возможно Вы не сталкивались с поддержкой чужого кода с интенсивными шаблонами. Пока он работает - все еще так-сяк, научиться пользоваться созданными можно. Но если заклинит - это ппц. В большинстве случаев снести нафиг и переписать с нуля = лучшее решение. Пусть оно будет хуже, пусть "не таким общим" но не "выносит моск". Учеников Александреску есть за что не любить  :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 07, 2013, 21:35
Учеников Александреску есть за что не любить  :)
Вместо этого лучше учиться самому. Понятно, что без нормального понимания шаблонов, сложно их сопровождать и тем более улучшать.



Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 07, 2013, 22:22
Зато сколько радости. Я вон сегодня вдохновлённый убрал 5 функций из своего проекта и заменил одной шаблонной :D
Писал на чистом наитии, заработала сразу. Счастлив.

PS sergek - всё как я и предполагал :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 08, 2013, 13:24
этим самым мы сохраняем информацию о "сериализуемых" данных, а всю технику прячем в методы базового (интерфейсного) класса. Когда надо передать (сериализовать) объект, вызывается интерфейсный метод send(), в котором последовательно перебирается список объектов упомянутых выше полиморфных типов, и для каждого вызывается метод objToBuf(). При восстановлении (приеме) вызывается метод receive(), выполняющий обратную задачу.
Идею понял, спасибо. Однако что делать для структур переменной длины, напр контейнеров? Их может быть великое множество, напр vectot<int>, vectot<float>  и еще 100 таких. Да и самих типов контейнеров не так уж мало. Создавать для каждого Boa элемент - явно нереально


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 08, 2013, 13:38
Вместо этого лучше учиться самому. Понятно, что без нормального понимания шаблонов, сложно их сопровождать и тем более улучшать.
"Диагноз товарища Саахова подтверждается". О как часто любитель шаблонов заявляет другим "Вы не понимаете!". Да, бывает и так, но гораздо чаще другое: он слишком увлекся "обобщениями" и забыл о задаче. Поэтому при малейшем изменении постановки или просто баге обобщения отливаются горючими слезами.

Хорошо, давайте добавим немного кода:
Код
C++ (Qt)
class iarchive : public archive
{
...
private:
QDataStream m_stream;
};
 
Членство QDataStream вызывает большие сомнения. Как минимум нужно перетаскивать все конструкторы QDataStream в iarchive. Почему не шаблон, по-моему здесь есть все основания
Код
C++ (Qt)
template <class BaseStream>
class iarchive : public archive
{
...
private:
BaseStream * m_stream;
};
 
???


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 08, 2013, 13:54
Почему не шаблон, по-моему здесь есть все основания
"Вы не понимаете!" (c)

Вполне нормальный пример. Для начала более чем достаточно, чтобы продемонстрировать направление. И копируйте код корректно, QDataStream совсем не iarchive находится.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 08, 2013, 14:14
"Вы не понимаете!" (c)

Вполне нормальный пример. Для начала более чем достаточно, чтобы продемонстрировать направление.
Во как! "продемонстрировать направление", не просто так "дерем из дуста". Впрочем я не удивлен, темплейтщик любит все раздувать  :)

Возвращаясь к проблеме сохранения полиморфных типов - предлагается такое решение
Код
C++ (Qt)
class A {
...
virtual void ReadWrite( archive & );
};
 
class B : public A {
...
virtual void ReadWrite( archive & );
};
Теперь при сохранении указателя на полиморфный тип вызываем a->ReadWrite(), (вместо оператора &), что конечно несколько неоднообразно. Критикуем, показываем как лучше


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 08, 2013, 14:52
Во как! "продемонстрировать направление", не просто так "дерем из дуста". Впрочем я не удивлен, темплейтщик любит все раздувать  :)
Шаблоны как раз исходный текст программы и сдувают :). И направление в дусте весьма дельное. Жаль, что его разжевывать приходится.

Теперь при сохранении указателя на полиморфный тип вызываем a->ReadWrite(), (вместо оператора &), что конечно несколько неоднообразно. Критикуем, показываем как лучше
Отличный способ, реализуйте :). И как в дусте до такого не догадались... Наверное побоялись прилепить к каждой структуре базовый класс, да еще с виртуальным методом. И, чтобы отвести глаза, наваяли кучу шаблонов с макросами :).


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: sergek от Февраль 08, 2013, 15:50
Идею понял, спасибо. Однако что делать для структур переменной длины, напр контейнеров? Их может быть великое множество, напр vectot<int>, vectot<float>  и еще 100 таких. Да и самих типов контейнеров не так уж мало. Создавать для каждого Boa элемент - явно нереально
Библиотека заточена под БД, поэтому основные структурные единицы - запись, таблица, blob. Для работы с контейнерами можно использовать параметризуемый тип BoaTable, который основан на стандартном векторе, например:
Код:
// запись
class TestRecord : public BoaInterface
{
public:
    bool         boolField;
    char         charField;
    short        shortField;
    int          intField;
    TestRecord();
};

// таблица
typedef BoaTable<TestRecord> TestTable;
// или для элементарных данных int
typedef BoaTable<Bint> TestTable;

Т.е., если надо передать контейнер, его сначала надо его перегнать в таблицу, которая уже все умеет.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 08, 2013, 17:29
Поэтому при малейшем изменении постановки или просто баге обобщения отливаются горючими слезами.
Ну слезы не самая большая цена за свою не компетентность. Не находите?

Членство QDataStream вызывает большие сомнения.
Да вы что? :)

Как минимум нужно перетаскивать все конструкторы QDataStream в iarchive.
Вы имеете ввиду еще один? Помимо того который я уже привел. Так это же демонстрашка. :)

Почему не шаблон, по-моему здесь есть все основания
Хорошо, тогда показывайте эти основания.
То что вы нарисовали ниже на основания не тянет.
Код
C++ (Qt)
template <class BaseStream>
class iarchive : public archive
{
...
private:
BaseStream * m_stream;  // Использовать голый указатель во всех местах дурной тон, тем более здесь. ;)
};
 
Я могу использовать этот шаблон, например, так:
Код
C++ (Qt)
irachive<QDomDocument> ar;
 


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 08, 2013, 17:35
Во как! "продемонстрировать направление", не просто так "дерем из дуста".
Не просто, а очень просто.
Мы взяли синтаксис буста и приспособили его к QDataStream и сделали это за несколько десяток простых строк.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 08, 2013, 18:00
Отличный способ, реализуйте :). И как в дусте до такого не догадались... Наверное побоялись прилепить к каждой структуре базовый класс, да еще с виртуальным методом.
Какой "каждой структуре" ??? Речь шла о полиморфных классах уже имеющих общую базу. Для них я предлагаю добавить в хедер строчку
Код
C++ (Qt)
virtual void ReadWrite( archive & ar ) { ar & (*this); }
 

И, чтобы отвести глаза, наваяли кучу шаблонов с макросами :).
И что, я должен быть безумно счастлив и должен бросаться использовать все наваянное?  :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 08, 2013, 18:08
Для них я предлагаю добавить в хедер строчку
А я предлагаю добавить класс:
Код
C++ (Qt)
class Serialize
{
public:
   virtual void ReadWrite( archive &ar ) = 0;
};
 
class A : public Base, public Serialize
{
public:
   virtual void ReadWrite( archive &ar );
};
 


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 08, 2013, 18:30
Ну слезы не самая большая цена за свою не компетентность. Не находите?
То есть если код труден для поддержки - некомпетентны поддерживающие? А почему Вы не допускаете мысли что он просто-напросто спланирован плохо, так сказать "жертва увлечения общностью"? Заметим что даже и в случае если Вы правы - возможно такого кода лучше не иметь.

Хорошо, тогда показывайте эти основания.
То что вы нарисовали ниже на основания не тянет.
А зачем Вы навесили уже 4 класса? Зачем тащить QDataStream (и каждый такой stream) если мы хотим от него только операторы?
Код
C++ (Qt)
template <class BaseStream>
class TArchive : public archive
{
TArchive( BaseStream * stream, int mode ) :
 m_stream(stream),
 m_mode(mode)
{
}
 
template<typename T>
TArchive &operator&( T &data )
{
switch (m_mode) {
case mode_Read:
  m_stream >> data;
  break;
 
case mode_Write:
  m_stream << data;
  break;
}  
return *this;
}
 
private:
BaseStream * m_stream;  
rw_mode m_mode,
};
 
Все в 1 хедере. Чего не хватает?

// Использовать голый указатель во всех местах дурной тон, тем более здесь. ;)
Ну значит QDataStream дурной тон - получает в конструкторе указатель на QIODevice :) Впрочем ссылка может приятнее - дело вкуса. Но не член точно  


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 08, 2013, 18:34
А я предлагаю добавить класс:
Тут согласен, смысл есть


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 08, 2013, 18:38
Какой "каждой структуре" ??? Речь шла о полиморфных классах уже имеющих общую базу.
А я вот и жду, узнаем мы наконец-то, что конкретно Вы собираетесь сериализовать и куда. Началось со структур по сети, потом "vectot<int>, vectot<float>", теперь и до полиморфных классов добрались. Или Вы хотите универсальный метод для всего сразу изобрести? :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 08, 2013, 18:42
То есть если код труден для поддержки - некомпетентны поддерживающие? А почему Вы не допускаете мысли что он просто-напросто спланирован плохо, так сказать "жертва увлечения общностью"? Заметим что даже и в случае если Вы правы - возможно такого кода лучше не иметь.
Да. Вы заказываете у человека готовый код и согласовываете использование шаблонов. Человек вам делает этот код на шаблонах.
А когда вы хотите сами в нем что-то изменить  оказывается, что вы в этом ничего не понимаете и сделать ничего не можете.
После этого вы обвиняете всех кроме себя. Учитесь или не заказываете такой код.

А зачем Вы навесили уже 4 класса?
Это называется иерархия классов. В дальнейшем, при развитии проекта, они наполнятся содержимым. :)


Зачем тащить QDataStream (и каждый такой stream) если мы хотим от него только операторы?
Мы хотим иметь архивы, которые позволяют сериализоваться в разные форматы (бинарный, текстовый, xml, json, ...)
Вот для примера я привел сериализацию в бинарный формат с использованием QDataStream?

Все в 1 хедере. Чего не хватает?
Это конечно большое достижение. :)
Кстати, у меня тоже все было в одном хедере.

Ну значит QDataStream дурной тон - получает в конструкторе указатель на QIODevice :)
Да.

Но не член точно  
Комплексы? :)
Конечно лучше создавать объект на куче руками, а потом героически не забывать его удалить. Это же круто. :)
Но это круто только в школьных проектах.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 08, 2013, 19:08
Учитесь или не заказываете такой код.
Ролью учителя лучше не увлекаться  :)

Это называется иерархия классов. В дальнейшем, при развитии проекта, они наполнятся содержимым. :)
...
Мы хотим иметь архивы, которые позволяют сериализоваться в разные форматы (бинарный, текстовый, xml, json, ...) Вот для примера я привел сериализацию в бинарный формат с использованием QDataStream?
А какой ф-ционал Вы хотите поместить напр в qds_iarchive? Зачем нужен класс который никакой смысловой нагрузки не несет? Все делает QDataStream (или др базовый stream), archive просто обертка. Хотите разные форматы - ну и создавайте "инстансы". Почему не потемплейтить если это к месту?

Конечно лучше создавать объект на куче руками, а потом героически не забывать его удалить. Это же круто. :)
Но это круто только в школьных проектах.
А я и не хочу ничего удалять, могу создать этот оберточный класс где хочу и мне это ничем не грозит.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 08, 2013, 19:18
Ролью учителя лучше не увлекаться  :)
А я никого не учу.
Перечитайте сообщение - там о другом. :)

А какой ф-ционал Вы хотите поместить напр в qds_iarchive?
Все. Здесь все делает бэкэнд QDataStream, а вот если я захочу в качестве бэкэнда использовать QDomDocument для сериализации в xml, то придется добавлять.

Почему не потемплейтить если это к месту?
Потому что потом это, возможно, придется кому-то сопровождать... Вы же сами выше жаловались. :)

А я и не хочу ничего удалять, могу создать этот оберточный класс где хочу и мне это ничем не грозит.
Ага-ага, а через пол года - захотел, а еще через пол года понял что течет, а еще через пол года переделок все починил. Перелопатив пол проекта.
А если еще и участников в проекте несколько,... можно и не завершиться никогда. :)
И заметь те это все только из-за комплексов.



Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 08, 2013, 19:29
Ага-ага, а через пол года - захотел, а еще через пол года понял что течет, а еще через пол года переделок все починил. Перелопатив пол проекта.
Ну бывает и так - и это нормально, если не слишком часто. Но почему Вы решили что членство "есть правильно"? Только потому что "кто-то так сказал/написал?" Я замечал что именно вещи "принимаемые без доказательств" ведут к самым тяжелым ошибкам и переделкам.

Ну ладно, то все "цветочки". А вот как сохранять указатели/ссылки на объекты если сам сериализуемый объект их не создает и не удаляет? Что Вы об этом думаете?



Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 08, 2013, 19:37
Потому что потом это, возможно, придется кому-то сопровождать... Вы же сами выше жаловались. :)
Пример: посидели с друзьями, приняли пару литров для оживления беседы. Не вижу в этом ничего плохого. А вот если каждый день спозаранку пьян - разница есть?  То же и здесь - открываете, а там аж кишит, все на темплейтах, живого места просто нету. Чему же это все посвящено? После неск дней "вникания" - да ничему путному, заигрался в общность, вот и все. Словом - злоупотребляют  :'(   


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 08, 2013, 19:55
Но почему Вы решили что членство "есть правильно"? Только потому что "кто-то так сказал/написал?"
Про кого вы все время говорите? Кто сказал/написал? Скажите имя этого вашего врага. :)
Я пишу на C++ с 91 года, у меня практически нет авторитетов в этом вопросе. Хотя я не считаю себя гуру c++.

Я замечал что именно вещи "принимаемые без доказательств" ведут к самым тяжелым ошибкам и переделкам.
Точно. Вот вы и не можете мне привести этих доказательств. Хотя пытаетесь учить: "Члену нет". :) Хотя никаких причин нет и быть не может.

А вот как сохранять указатели/ссылки на объекты если сам сериализуемый объект их не создает и не удаляет?
Чуть позже напишу.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 08, 2013, 20:56
Точно. Вот вы и не можете мне привести этих доказательств. Хотя пытаетесь учить: "Члену нет". :)
Неправда, я таким жестокостям с членом не учил! :) . Ваше утверждение "голый указатель = плохо" имеет никак не больше доводов. Если QDataStream использует порочный (на Ваш взгляд) подход, то где же те грабли на которые (якобы) все должны наступать? Почему я о них никогда не слышал?

Я исходил из простых соображений. Если archive - обертка, то чего ради он должен брать на себя конструктор/деструктор базового stream? Почему я не могу зарядить просто базовый, а должен объявлять archive? Что если я захочу записать в тот же файл что-то вне рамок сериализации (напр TOC) ?




Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 08, 2013, 21:12
Неправда, я таким жестокостям с членом не учил! :) .
Оно для вас было сомнительно и поэтому вы заменили его указателем.

Ваше утверждение "голый указатель = плохо" имеет никак не больше доводов.
Про голые указатели вы никогда не слышали? Выходите скорее из пещеры. Только ленивый не говорит про то, что сейчас можно и нужно писать программы без голых указателей, что бы исключить утечки ресурсов в программе. Уже давно придумали "умные указатели", которые сильно облегчают жизнь при написании больших систем.

Если QDataStream использует порочный (на Ваш взгляд) подход, то где же те грабли на которые (якобы) все должны наступать?
Потому что в С++ есть ссылки, которые для этого подходят куда лучше.

Я исходил из простых соображений. Если archive - обертка, то чего ради он должен брать на себя конструктор/деструктор базового stream?
Почему обертка, это полноценный класс? Поток это поток, архив это архив. Не надо их сравнивать.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Bepec от Февраль 08, 2013, 21:34
Не соглашусь по умным указателям.

Умные это конечно хорошо, облегчает жизнь и прочая. Но голые они как то и быстрее и лучше и ничего плохого они не сделают (найти где у голого утекает память проще пареной репы.).

Другой вопрос, что это зависит от требований. Пять тысяч голых и пять тысяч умных это ООООЧЕЕНЬ большая разница. Удобная, хорошая, но разница.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 08, 2013, 21:35
Другой вопрос, что это зависит от требований. Пять тысяч голых и пять тысяч умных это ООООЧЕЕНЬ большая разница. Удобная, хорошая, но разница.
Ох уж эти мифы и легенды... ;)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 08, 2013, 21:39
А вот как сохранять указатели/ссылки на объекты если сам сериализуемый объект их не создает и не удаляет?
Необходимо ввести уникальные идентификаторы объектов.
При сохранении, вместо указателя сохранять его идентификатор, а при чтении получать по идентификатору указатель на объект.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: ViTech от Февраль 08, 2013, 21:50
При сохранении, вместо указателя сохранять его идентификатор, а при чтении получать по идентификатору указатель на объект.
А насколько плохо в качестве такого идентификатора использовать текущий адрес объекта в памяти? (Допустим, что памяти хватает и все нужные объекты там уже находятся).


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 08, 2013, 21:56
А насколько плохо в качестве такого идентификатора использовать текущий адрес объекта в памяти? (Допустим, что памяти хватает и все нужные объекты там уже находятся).
Есть простой но ужасный довод что это не катит (4 латинских буквы)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 08, 2013, 21:57
А насколько плохо в качестве такого идентификатора использовать текущий адрес объекта в памяти? (Допустим, что памяти хватает и все нужные объекты там уже находятся).
Да что угодно можно использовать, главное что этот идентификатор уникально определял объект.
Это может быть тот же порядковый номер в коллекции.

Есть список указателей на объекты текстур и есть куча объектов полигонов, в которых есть указатели на эти объекты текстуры.
Мы сохраняем объекты текстуры в том же порядке, а при сохранении полигонов, вместо указателя сохраняем порядковый номер текстуры.


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 09, 2013, 11:12
Есть список указателей на объекты текстур и есть куча объектов полигонов, в которых есть указатели на эти объекты текстуры.
Мы сохраняем объекты текстуры в том же порядке, а при сохранении полигонов, вместо указателя сохраняем порядковый номер текстуры.
Ну все-таки указатель на текстуру хранят для объекта, а не для каждого полигона (дороговато выйдет, полигонов много  :)). Да, этот способ часто применяется, но

- нужно хранить/отслеживать коллекции

- при де-сериализации я не могу восстановить указатель немедленно (хз текстура загружена или нет). Нужна фаза "восстановления ссылок/связок"

- загружая текстуру (объект на который ссылаются) я не могу от нее немедленно избавиться (напр если файл картинки отсутствует). До того как разрешены ссылки я вынужден сидеть с невалидным объектом текстуры, что довольно мерзко

- возникают трудности с сериализацией части данных. Хотя эти трудности объективны - от этого не легче

В общем, типовая ситуевина когда стройная архитектура классов превращается в море латок, а умничание быстро заканчивается  :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 09, 2013, 11:16
В общем, типовая ситуевина когда стройная архитектура классов превращается в море латок, а умничание быстро заканчивается  :)
Мне жаль ваших заказчиков. :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Igors от Февраль 09, 2013, 11:21
Мне жаль ваших заказчиков. :)
Спасибо за сочувствие - но я просто рассказал что ожидает на пути который Вы предложили  :)


Название: Re: Сериализация (как сделать поудобнее)
Отправлено: Old от Февраль 09, 2013, 14:44
Спасибо за сочувствие - но я просто рассказал что ожидает на пути который Вы предложили  :)
Это если вы его будете реализовывать... И я думаю это еще далеко не все проблемы. ;)

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

Давайте углубимся в детали.

- нужно хранить/отслеживать коллекции
Это бесспорно хит. :)
Так же, можно просить заказчика увеличить стоимость проекта на треть в связи с тем, что вам придется хранить и отслеживать все объекты системы, и самое главное не забывать их удалять при завершении ее работы.

- при де-сериализации я не могу восстановить указатель немедленно (хз текстура загружена или нет). Нужна фаза "восстановления ссылок/связок"
А нам для восстановления указателя вообще не нужны данные картинки, поэтому никаких "фаз восстановления ссылок/связей" здесь не нужно. (Заказчик бы плакал от такой красивой фразы). :)
Нужно разделить класс текстур и класс картинок. Объект текстуры можно создать сейчас, а данные картинки загрузить через десять с половиной секунд. Или вообще не загружать, ведь они нужны только для визуализации. К тому же текстура может иметь возможность хранить изображения разных размеров.
Поэтому, идея текстура == картинка изначально, эммм, недальновидная. :)

- загружая текстуру (объект на который ссылаются) я не могу от нее немедленно избавиться (напр если файл картинки отсутствует). До того как разрешены ссылки я вынужден сидеть с невалидным объектом текстуры, что довольно мерзко
Не могу ничего пояснить, потому что не могу представить что вы имеете ввиду.

- возникают трудности с сериализацией части данных. Хотя эти трудности объективны - от этого не легче
Какие трудности и почему они объективные?

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