Russian Qt Forum
Ноябрь 23, 2024, 15:18 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3 ... 8   Вниз
  Печать  
Автор Тема: Сериализация (как сделать поудобнее)  (Прочитано 48112 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Февраль 06, 2013, 15:37 »

Добрый день

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

Спасибо
Записан
Bepec
Гость
« Ответ #1 : Февраль 06, 2013, 15:49 »

У структуры делать оператор. Тогда всё сведётся к out << myStruct;.

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

Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Февраль 06, 2013, 15:57 »

У структуры делать оператор. Тогда всё сведётся к out << myStruct;.
Я и спрашиваю как избежать повтора довольно большого куска в операторах >> и << для большой структуры

Хотя конечно в более крутом смысле вашего вопроса (как обычно),
"Циля, он еще и поет"  Улыбающийся
Записан
Bepec
Гость
« Ответ #3 : Февраль 06, 2013, 16:02 »

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

Сомнительный - потому что я уже встречал программиста это использующего. Впечатлений много. Особенно после описания функции в 72 (Я ЗАПОМНИЛ!!!) строки, чтобы получить 3 поля структуры.
Записан
alexis031182
Гость
« Ответ #4 : Февраль 06, 2013, 16:13 »

...
Ну и если чего-то изменилось, сбивать 2 копии утомительно. Как бы это дело обобщить?
Добрый день. Ну тут как бы всё равно речь о двух и более машинах, значит и мест правки кода будет несколько. Другое дело, что это, наверное, можно свести к одной либе, копия файла которой будет у сервера/серверов и клиентов.

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

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

Сообщений: 858



Просмотр профиля
« Ответ #5 : Февраль 06, 2013, 17:02 »

Я и спрашиваю как избежать повтора довольно большого куска в операторах >> и << для большой структуры
Ручками, ручками Улыбающийся. А если ручками лень, то написать парсер, который по исходникам сгенерит сериализаторы. Которые потом все равно придется ручками править Веселый.

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

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

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

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Февраль 06, 2013, 17:36 »

Ручками, ручками Улыбающийся. А если ручками лень, то написать парсер, который по исходникам сгенерит сериализаторы. Которые потом все равно придется ручками править Веселый.
Ручки у барышень которые не работают Улыбающийся Неужто все так безнадежно? Ну почему напр не так
Код
C++ (Qt)
void MyStruct::ReadWrite( QDataStream & strm, bool modeWrite )
{
ReadWriteInt(strm, &mID, modeWrite);
ReadWriteQString(strm, &mName, modeWrite);
...
}
« Последнее редактирование: Февраль 06, 2013, 17:38 от Igors » Записан
Bepec
Гость
« Ответ #7 : Февраль 06, 2013, 17:47 »

Эм... Вы только что считай написали оператор Де/Сериализации Веселый

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

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

PS кстати, а почему не так
Код:
void MyStruct::ReadWrite( QDataStream & strm, bool modeWrite )
{
if (modeWrite)
    out >>  this.intPoleMyStruct >> this.qstringPoleMyStruct ... ;
else
 ...
}
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #8 : Февраль 06, 2013, 18:12 »

Код
C++ (Qt)
void MyStruct::ReadWrite( QDataStream & strm, bool modeWrite )

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

А написать отдельные методы на чтение и на запись, не самое страшное, что есть в сериализации Улыбающийся.
Записан

Пока сам не сделаешь...
Bepec
Гость
« Ответ #9 : Февраль 06, 2013, 18:46 »

ViTech продолжите свою мысль пожалуйста. Где же тогда создавать сериализацию и десериализацию? и псевдокод использования пожалуйста, если не сложно.
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #10 : Февраль 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
Тот, кто будет читать/записывать, знает, каким способом он это сделает, подключит необходимые файлы и вызовет нужные функции. Может много хидеров подключить придется, зато ничего лишнего не будет. Можно и сгруппировать операции связанных структур в один файл, а не каждую в отдельный, но в общем смысл такой.
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Февраль 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
Записан
sergek
Гипер активный житель
*****
Offline Offline

Сообщений: 872


Мы должны приносить пользу людям.


Просмотр профиля
« Ответ #12 : Февраль 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. Полная документация (руководство программиста, справочник), примеры для Borland C++Builder и Qt в Windows и Unix. Правда, давно не компилировал под nix-системами (последний раз проверял под FreeBSD 4.5 и Fedora 10 (или 12?).
Но если кого заинтересует, проверю в ubunta 12.04.01.
Записан

Qt 5.13.0 Qt Creator 5.0.1
Win10, Ubuntu 20.04
sergek
Гипер активный житель
*****
Offline Offline

Сообщений: 872


Мы должны приносить пользу людям.


Просмотр профиля
« Ответ #13 : Февраль 06, 2013, 20:53 »

Кстати, по указанному адресу есть и скомпилированные примеры, можно запустить и попробовать.
Записан

Qt 5.13.0 Qt Creator 5.0.1
Win10, Ubuntu 20.04
Bepec
Гость
« Ответ #14 : Февраль 06, 2013, 21:01 »

В принципе ничего нового, кроме шаблонного метода Igors.

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

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

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

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

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

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


to sergek:

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

Или я не уловил вашу систему и у вас сериализуются типы по их текстовому названию и вызывается соответствующий сериализатор?
Записан
Страниц: [1] 2 3 ... 8   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.122 секунд. Запросов: 23.