Очень простая сериализация для C++

ОГЛАВЛЕНИЕ

В данной статье описана реализация легкого механизма для сохранения объектов C++ в XML или двоичные форматы.

•    Скачать исходники с файлами проекта для VS2003 - 31.6 Кб
•    Скачать исходники с файлами проекта для VS2008 - 31.6 Кб
•    Скачать исходники и двоичный файл выпуска - 313 Кб (содержит демонстрационный проект GUI(графический пользовательский интерфейс) для VS2003)

Введение

В данной статье описана реализация легкого механизма для сохранения объектов C++ в XML или двоичные форматы. Статьи такого рода обычно не порождают много графического содержимого, отсюда много вставленных фрагментов кода.

Пример кода содержит проекты VS2003 и VS2008, формирующие консольное приложение для блочного теста. /W4 используется везде.

Кроме того, есть очень простое приложение MFC Контакты, отображающее содержимое в гибридном элементе управления сетка-дерево. Задача приложения GUI – показать, как:
•    Расширить маршалирование очень простой сериализации для ваших собственных классов
•    Использовать простой контроль версий
•    Очень простая сериализация справляется с реальным кодом – защищенными конструкторами, виртуальными пустыми функциями, и т.д.
•    Создание динамических объектов – и как очень простая сериализация обрабатывает ошибки

Когда использовать

У данной методики есть много применений, включая сохранение настроек программы, сохранение состояния для операций отмены/восстановления, автоматической активации форматов файла XML для данных приложения, связи клиент-сервер (т.е. пакеты в проводе) и хранения нереляционных данных в виде XML в базах данных SQL.

Основные особенности

1.    Весь код соответствует ISO C++ и переносим
2.    Не требует, чтобы постоянные классы имели общий базовый класс
3.    Не требует компиляции с поддержкой информации о типе времени исполнения
4.    Соблюдает существующий контроль доступа для конструкторов и деструкторов
5.    Сериализует указатели на классы/структуры, поддающиеся сериализации
6.    Правильно восстанавливает содержимое контейнеров указателей на полиморфные объекты
7.    Делает упор на проверку во время компиляции, чтобы минимизировать ошибки при выполнении
8.    Макросы используются только для краткости и направляются в отлаживаемый код
9.    Реализация полностью встроенная – надо только включить заголовочные файлы очень простой сериализации посредством #include
10.    Очень просто добавить новые форматы хранения - JSON, к примеру

Ограничения

1.    Требует, чтобы сериализуемые классы имели пустой конструктор
2.    Текущая реализация предполагает, что сериализация производится в одном потоке – немного нужно, чтобы добавить безопасность потоков
3.    Явно не поддерживает сериализацию типов-указателей 'C', особенно void* и friend(дружественный)
4.    Пока не реализовано хранилище строки UNICODE для XML
5.    Нет теоретических препятствий к использованию очень простой сериализации с множественным наследованием, но вообще не тестировалось

Соглашения

Во избежание бесчисленных повторений принимается, что любой класс C0 является базовым классом в произвольной иерархии, где C1 унаследован от C0, а C2, в свою очередь, унаследован от C1. Корневой класс описывает «наименее производный» класс. RTTI - информация о типе времени исполнения, и это сокращение применяется при рассмотрении того, как хранить запись имен классов и информацию о наследовании во время выполнения. Следовательно, имеется:

Замечание о макросах и шаблонах

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

Макросы ESS_REGISTER, ESS_RTTI и ESS_STREAM используют оператор преобразования в строку (#) для порождения строк из имен классов и экземпляров. Это хорошо, так как уменьшает возможность для ошибки. ESS_RTTI также объявляет дружественный шаблонный класс фабрики, отвечающий за создание новых экземпляров в куче, следовательно, у него есть доступ к защищенным/закрытым конструкторам и деструкторам. Это значительно упрощает применение очень простой сериализации к существующему коду, принося решительную пользу.

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

// тривиальный
#define ESS_ROOT(rootname) typedef ess::root<rootname> ess_root;

// направляется в шаблонную функцию
#define ESS_STREAM(stream_adapter,class_member)        \
    ess::stream(stream_adapter,class_member,#class_member)

// и чуть более сложная комбинация ...
#define ESS_RTTI(classname,rootname)\
friend ess::CFactory<classname,rootname>; \
virtual const char* get_name()\
{ return ess::get_name_impl<classname>(#classname); }\
static ess::class_registry<classname>* get_registry()\
{ return ess::get_registry_root<classname,rootname>(#rootname); }

ESS_RTTI – самый сложный из макросов очень простой сериализации.

Еще одно философское заявление: шаблоны прекрасны, но метапрограммирование шаблона – нет. Почему? Метапрограммирование шаблона не выдерживает тест отладчика.