| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > 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 | ||
Конечно. Твой подход годится только для очень простых структур (где только независимые переменные). Да и то, а если это не-плоские классы? Или строки (не char[] а string)? Нельзя их тупо по адресу писать-читать. Пардон, строки у тебя предусмотрены... Однако все остальное - в силе. Представь, у тебя есть контейнер, где частью элемента являются ссылки на какие-то другие классы (которые тоже сериализуются). Но самое главное (ИМХО) - скрытое хранение адресов элементов класса где-то еще лишает его объекты семантики значений - или нужно потратить усилия, чтобы ее обеспечить. Неоправданное усложнение, по-моему. Сама по себе сериализация становится сложной, только когда начинаешь думать о поддержке версий. А так - чем проще, тем лучше. Мне кажется, в данном случае лучше подходить проще: сделать в базовом классе твоей иерархии метод Serialize (как в MFC) и переопределять его + написать хелперы для сериализации контейнеров, строк и простых типов. Здесь по крайней мере соблюдается единство места: все поведение класса локализовано и все изменения производятся в одном месте. Вроде есть еще boost::seriazlize, но сама не пользовалась. |
| Автор: azesmcar 27.12.2005, 09:55 | ||||||||
Немало думал над этим, пока что (во всяком случае для меня) единственный приемлемый вариант был такой...это для серилизации некоторых структур...например есть функция которая должна записывать структуры и классы в файл...
Реализуем класс наподобие...
А функцуя WriteToFileStruct будет иметь следующий вид
Ничего более универсального придумать не смог...да вряд ли есть способ..если в классе есть динамическое выделение памяти |
| Автор: Chaos A.D. 27.12.2005, 11:31 |
| Давно не думал в этом направлении, так что могу спороть чушь... По-моему, если у тебя есть иерархия, и тебе просто нужно сделать её всю сериализуемой, то добавь в неё пару виртуальных функций типа "Писать" - "Читать", и все. Ну а указатели сделать умными, правда это не очень хорошо, придется модифицировать всю иерархию... С указателями я бы попытался отвертеться с помощью свойств - Специализирую шаблон для типа указателя, и для него сохраняю то, на что он указывает. И не стоит заморачиваться с решением общего случая сериализации - лучше чем у Boost-а вряд-ли получится. В любом случае, у тебя в твоем примере каждый производный класс должен знать о том, что его будут сериализовать (я имею ввиду твой конструктор). Ну а так, перегружаешь функцию, и все. Если напрягает то, что класс должен знать о том, куда его пишут, сделай функцию, которая бы просто преобразовала объект в последовательность битов, и наоборот. А всё остальное выноси из класса. Можно так-же заюзать паттерн Factory, производящий объекты из строки. Страуструп в своей книге пару раз упомянает объектный ввод/вывод - Тоже интересная идея - делаешь конструктор, создающий объект из потока ввода. И потом просто читаешь файл: при прочтении строки, которая сопоставлена с определенной make-функцией, вызываешь её, а она, в свою очередь, вызывает конструктор с твоим файлом. Ну а вообще, я бы заюзал boost::serialize. В нем, как говорится, All Included. Самое приятное, что boost::serialize не интрузивен, т.е. твои классы вообще не должны ничего на себя брать. |
| Автор: Earnest 27.12.2005, 12:53 | ||||
Интересная, но не симметричная - куда запись запихать? Приходится делать функцию Save, а тогда уж логичнее Load для симметрии. Правда, ее (Load) можно вызывать и из конструктора, чтобы можно было писать CMyObject Obj(in); - что, действительно, иногда удобно. Добавлено @ 12:54
Лучше (в С++) в базовый поток (istream). |
| Автор: Chaos A.D. 27.12.2005, 14:03 | ||||
Мне почему-то вспомнился отрывок из книги Джеффа Элджера C++ For Real Programmers...
|
| Автор: Earnest 27.12.2005, 15:22 |
| Не читала... Но книжка, наверное, хорошая. А вообще, разве ты не чувствуешь, что симметричный код, как-то... более соответствует мировой гармонии? |
| Автор: Chaos A.D. 27.12.2005, 16:45 | ||||
Хы, оттуда же цитата, которую я полностью поддерживаю:
Вообще, я сам люблю, когда все в проекте гармонично. Например, когда я думаю об одном из своих проектов, который мне приходится сопровождать, у меня в голове мигом рисуется схема того, какая часть программы чего использует, как что-к-чему относится и зависит. И у этого проекта архитектура настолько запутанная, что в голове сплошная мешанина. Поэтому, я ничего не имею против красивого и правильного "симметричного" кода. Просто, цитату смешную вспомнил. |
| Автор: Chaos A.D. 27.12.2005, 16:56 |
| А книга есть в электронном формате, но она, как бы вроде для тех, кто только что прочел Страуструпа что-нибудь другое для начинающих. Хотя после Александреску, Саттера и Мейерса перечитывать довольно любопытно. Как-то нестандартно мыслит мужик (хоть и примеры немного надуманные). |
| Автор: GKosh 31.12.2005, 12:53 | ||||
| Спасибо всем, boost::serialize гляну, но в моем случае мне нужен очень четкий контроль за создаваемой структурой. Сериализоваться будет дерево, с большим числом полиморфных классов внутри. Соответсвенно должна быть возможность:
и этого должно быть достаточно для каскада динамического выделения памяти под все более удаленные от корня объекты - вообщем, наверное лучше писать самому.
Ну да и строки, и объекты ввиде членов класса и даже вложенные списки - все это учитывается. Что меня больше всего смущает, так это форма в которой я храню указатели на члены класса: 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 | ||
это нужно заменить на varint\ boost::variant \ boost::any. После этого смотри как сериализация устроена в бусте или MFC |
| Автор: GKosh 31.12.2005, 16:20 | ||
Сериализация 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 | ||
нет. При изспользование boost::any клиент олжен знать кто там хранится. Тут может пригодится boost::variant(это не тот вариант о котором ты подумал. Это намного круче) |