Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Serialize/Deserialize


Автор: GKosh 27.12.2005, 09:00
Необходимо было реализовать сериализацию для иерархии собственных классов, сделал так:
Базовый класс всей иерархии имеет динамический список структур вида

struct PROPERTY {
const char* sProperty; // имя переменной - члена класса
void** pProperty; // указатель на переменную
SERIA_TYPE Type; // метка типа переменной (int, float, string, object)
};

по идее - это как бы карта свойств объекта, базовый класс реализует так же метод:
void Register_Property (const char* a_name, void* a_pointer, SERIA_TYPE a_type)
для заполнения этого списка указателями на переменные.
Каждый класс иерархии в своем конструкторе регистрирует свои переменные этой функцией, после чего наследованные методы базового класса Serialize/Deserialize уже спокойно записывают, считывают заполненный список указателей.
Таким образом практически вся реализация ser/deser находится только в базовом классе.
Вопрос - может быть что-нибудь есть дурного в таком подходе, пока я его еще не отладил но у меня имеются некоторые сомнения в его надежности. Возможно есть проверенные методы о которых я, к сожалению, не в курсе.

Заранее благодарен,
Константин.

Автор: Earnest 27.12.2005, 09:28
Цитата(GKosh @ 27.12.2005, 09:00)
Вопрос - может быть что-нибудь есть дурного в таком подходе

Конечно. Твой подход годится только для очень простых структур (где только независимые переменные). Да и то, а если это не-плоские классы? Или строки (не char[] а string)? Нельзя их тупо по адресу писать-читать. Пардон, строки у тебя предусмотрены... Однако все остальное - в силе. Представь, у тебя есть контейнер, где частью элемента являются ссылки на какие-то другие классы (которые тоже сериализуются).
Но самое главное (ИМХО) - скрытое хранение адресов элементов класса где-то еще лишает его объекты семантики значений - или нужно потратить усилия, чтобы ее обеспечить. Неоправданное усложнение, по-моему.

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

Мне кажется, в данном случае лучше подходить проще: сделать в базовом классе твоей иерархии метод Serialize (как в MFC) и переопределять его + написать хелперы для сериализации контейнеров, строк и простых типов. Здесь по крайней мере соблюдается единство места: все поведение класса локализовано и все изменения производятся в одном месте.
Вроде есть еще boost::seriazlize, но сама не пользовалась.

Автор: azesmcar 27.12.2005, 09:55
Цитата
Мне кажется, в данном случае лучше подходить проще: сделать в базовом классе твоей иерархии метод Serialize (как в MFC) и переопределять его + написать хелперы для сериализации контейнеров, строк и простых типов. Здесь по крайней мере соблюдается единство места: все поведение класса локализовано и все изменения производятся в одном месте.


Немало думал над этим, пока что (во всяком случае для меня) единственный приемлемый вариант был такой...это для серилизации некоторых структур...например есть функция которая должна записывать структуры и классы в файл...
Код

void WriteToFileStruct(...);

Реализуем класс наподобие...
Код

class CSerilizibleClass
{
public:
   void Serilize(char *pBuffer, int& nBufferSizeRef) = 0;
   void Unserilize(const char *pBuffer, int nBufferSize) = 0;
};

А функцуя WriteToFileStruct будет иметь следующий вид
Код

void WriteToFileStruct(CSerilizibleClass* pClass)
{
   pClass->Serilize(...);
   //...
}

Ничего более универсального придумать не смог...да вряд ли есть способ..если в классе есть динамическое выделение памяти

Автор: Chaos A.D. 27.12.2005, 11:31
Давно не думал в этом направлении, так что могу спороть чушь... По-моему, если у тебя есть иерархия, и тебе просто нужно сделать её всю сериализуемой, то добавь в неё пару виртуальных функций типа "Писать" - "Читать", и все. Ну а указатели сделать умными, правда это не очень хорошо, придется модифицировать всю иерархию... С указателями я бы попытался отвертеться с помощью свойств - Специализирую шаблон для типа указателя, и для него сохраняю то, на что он указывает.

И не стоит заморачиваться с решением общего случая сериализации - лучше чем у Boost-а вряд-ли получится. В любом случае, у тебя в твоем примере каждый производный класс должен знать о том, что его будут сериализовать (я имею ввиду твой конструктор). Ну а так, перегружаешь функцию, и все. Если напрягает то, что класс должен знать о том, куда его пишут, сделай функцию, которая бы просто преобразовала объект в последовательность битов, и наоборот. А всё остальное выноси из класса.

Можно так-же заюзать паттерн Factory, производящий объекты из строки. Страуструп в своей книге пару раз упомянает объектный ввод/вывод - Тоже интересная идея - делаешь конструктор, создающий объект из потока ввода. И потом просто читаешь файл: при прочтении строки, которая сопоставлена с определенной make-функцией, вызываешь её, а она, в свою очередь, вызывает конструктор с твоим файлом.
Ну а вообще, я бы заюзал boost::serialize. В нем, как говорится, All Included. Самое приятное, что boost::serialize не интрузивен, т.е. твои классы вообще не должны ничего на себя брать.

Автор: Earnest 27.12.2005, 12:53
Цитата(Chaos @ 27.12.2005, 11:31)
Страуструп в своей книге пару раз упомянает объектный ввод/вывод - Тоже интересная идея - делаешь конструктор, создающий объект из потока ввода

Интересная, но не симметричная - куда запись запихать? Приходится делать функцию Save, а тогда уж логичнее Load для симметрии. Правда, ее (Load) можно вызывать и из конструктора, чтобы можно было писать CMyObject Obj(in); - что, действительно, иногда удобно.

Добавлено @ 12:54
Цитата(Chaos @ 27.12.2005, 11:31)
сделай функцию, которая бы просто преобразовала объект в последовательность битов, и наоборот.

Лучше (в С++) в базовый поток (istream).

Автор: Chaos A.D. 27.12.2005, 14:03
Цитата(Earnest @ 27.12.2005, 12:53)
Интересная, но не симметричная...

Мне почему-то вспомнился отрывок из книги Джеффа Элджера C++ For Real Programmers...
Цитата

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

smile

Автор: Earnest 27.12.2005, 15:22
Не читала... smile
Но книжка, наверное, хорошая.
А вообще, разве ты не чувствуешь, что симметричный код, как-то... более соответствует мировой гармонии? smile

Автор: Chaos A.D. 27.12.2005, 16:45
Цитата(Earnest @ 27.12.2005, 15:22)
Не читала... 
Но книжка, наверное, хорошая.
А вообще, разве ты не чувствуешь, что симметричный код, как-то... более соответствует мировой гармонии?


Хы, оттуда же цитата, которую я полностью поддерживаю:
Цитата

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

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

Автор: Chaos A.D. 27.12.2005, 16:56
А книга есть в электронном формате, но она, как бы вроде для тех, кто только что прочел Страуструпа что-нибудь другое для начинающих. Хотя после Александреску, Саттера и Мейерса перечитывать довольно любопытно. Как-то нестандартно мыслит мужик (хоть и примеры немного надуманные).

Автор: GKosh 31.12.2005, 12:53
Спасибо всем,
boost::serialize гляну, но в моем случае мне нужен очень четкий контроль за создаваемой структурой.
Сериализоваться будет дерево, с большим числом полиморфных классов внутри. Соответсвенно должна быть возможность:
Код

CMyTree* mytree = new CMyTree(tree.txt);

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

Цитата(Earnest @ 27.12.2005, 09:28)
Да и то, а если это не-плоские классы? Или строки

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

struct PROPERTY {
const char* sProperty; // имя переменной - члена класса
void** pProperty; // указатель на переменную
SERIA_TYPE Type; // метка типа переменной (int, float, string, object)
};

Вон тот вот void** мне признаться не нравится - можно было бы как-то иначе указывать на член класса (помним, что членом класса может быть указатель на объект другого класса) без void, но чтобы и на int и на string и на условный CMyTree - полагаю использование полиморфной оболочки на int - это будет перебор.

Автор: DeadSoul 31.12.2005, 13:15
Цитата(GKosh @ 27.12.2005, 09:00)
void** pProperty; // указатель на переменную
SERIA_TYPE Type; // метка типа переменной (int, float, string, object)

это нужно заменить на varint\ boost::variant \ boost::any.

После этого смотри как сериализация устроена в бусте или MFC

Автор: GKosh 31.12.2005, 16:20
Цитата(DeadSoul @ 31.12.2005, 13:15)
После этого смотри как сериализация устроена в бусте или MFC

Сериализация boost'а мне не годится, нужно, чтобы сэйв можно было бы при необходимости редактировать или даже составлять от руки - нужен парсер команд, навроде color = 100; m_root += CNode и.т.п. Впрочем не суть. boost::any гляну - это как variant в VBasic'е? Или он хранит информация о своем "настоящем" типе?

Автор: Daevaorn 31.12.2005, 16:37
GKosh
Цитата

Или он хранит информация о своем "настоящем" типе?

Там голый void*, но использует RTTI для опредеения текущего типа и конвертации в нужный.

Автор: DeadSoul 31.12.2005, 16:39
Цитата(GKosh @ 31.12.2005, 16:20)
Впрочем не суть. boost::any гляну - это как variant в VBasic'е?

нет. При изспользование boost::any клиент олжен знать кто там хранится. Тут может пригодится boost::variant(это не тот вариант о котором ты подумал. Это намного круче)

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)