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

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

Страниц: 1 ... 4 5 [6] 7 8   Вниз
  Печать  
Автор Тема: Сериализация (как сделать поудобнее)  (Прочитано 48160 раз)
sergek
Гипер активный житель
*****
Offline Offline

Сообщений: 872


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


Просмотр профиля
« Ответ #75 : Февраль 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().
Все остальное (много чего) прозрачно для разработчика и не требует кодирования под каждую задачу.
« Последнее редактирование: Февраль 07, 2013, 21:37 от sergek » Записан

Qt 5.13.0 Qt Creator 5.0.1
Win10, Ubuntu 20.04
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #76 : Февраль 07, 2013, 21:24 »

Непонятно только, что тут вызывает у Вас такой антагонизм. Ну шаблоны, ну typedef, ну не пользуйтесь, раз не нравится. Как бы свободу выбора, хотя бы в этом, пока ещё не отменяли Улыбающийся
Возможно Вы не сталкивались с поддержкой чужого кода с интенсивными шаблонами. Пока он работает - все еще так-сяк, научиться пользоваться созданными можно. Но если заклинит - это ппц. В большинстве случаев снести нафиг и переписать с нуля = лучшее решение. Пусть оно будет хуже, пусть "не таким общим" но не "выносит моск". Учеников Александреску есть за что не любить  Улыбающийся
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #77 : Февраль 07, 2013, 21:35 »

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

Записан
Bepec
Гость
« Ответ #78 : Февраль 07, 2013, 22:22 »

Зато сколько радости. Я вон сегодня вдохновлённый убрал 5 функций из своего проекта и заменил одной шаблонной Веселый
Писал на чистом наитии, заработала сразу. Счастлив.

PS sergek - всё как я и предполагал Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


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

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

Сообщений: 11445


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

Сообщений: 858



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

Почему не шаблон, по-моему здесь есть все основания
"Вы не понимаете!" (c)

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

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

Сообщений: 11445


Просмотр профиля
« Ответ #82 : Февраль 08, 2013, 14:14 »

"Вы не понимаете!" (c)

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

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

Сообщений: 858



Просмотр профиля
« Ответ #83 : Февраль 08, 2013, 14:52 »

Во как! "продемонстрировать направление", не просто так "дерем из дуста". Впрочем я не удивлен, темплейтщик любит все раздувать  Улыбающийся
Шаблоны как раз исходный текст программы и сдувают Улыбающийся. И направление в дусте весьма дельное. Жаль, что его разжевывать приходится.

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

Пока сам не сделаешь...
sergek
Гипер активный житель
*****
Offline Offline

Сообщений: 872


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


Просмотр профиля
« Ответ #84 : Февраль 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;

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

Qt 5.13.0 Qt Creator 5.0.1
Win10, Ubuntu 20.04
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #85 : Февраль 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;
 
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



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

Во как! "продемонстрировать направление", не просто так "дерем из дуста".
Не просто, а очень просто.
Мы взяли синтаксис буста и приспособили его к QDataStream и сделали это за несколько десяток простых строк.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


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

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

И, чтобы отвести глаза, наваяли кучу шаблонов с макросами Улыбающийся.
И что, я должен быть безумно счастлив и должен бросаться использовать все наваянное?  Улыбающийся
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #88 : Февраль 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 );
};
 
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #89 : Февраль 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 Улыбающийся Впрочем ссылка может приятнее - дело вкуса. Но не член точно  
« Последнее редактирование: Февраль 08, 2013, 18:32 от Igors » Записан
Страниц: 1 ... 4 5 [6] 7 8   Вверх
  Печать  
 
Перейти в:  


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