Есть туториал на официальном сайте.
И все бы хорошо, если не множество подводных камней.
Все началось с того, что я пропустил в туториале объявления макросов (T — идентификатор класса):
BOOST_SERIALIZATION_ASSUME_ABSTRACT(T) — для сериализации абстрактных классов (я объявлял его до определения функций сериализации).
BOOST_SERIALIZATION_SPLIT_FREE(T) — если определять сериализацию
через save/load — этот макрос для non intrusive версии сериализации, для
intrusive (т.е. метод класса) BOOST_SERIALIZATION_SPLIT_MEMBER().
Объявляется после определения функций сериализации. Назначение —
генерирует инлайн функцию serialize, которая используется при
(де)сериализации для выбора save/load. Запись и чтение в
boost::serialization реализованы через один оператор & (амперсанд)
который в зависимости от архива, к которому применяется (ахив то
шаблонный надо объявлять) интерпретируется как оператор записи или
чтения.
BOOST_CLASS_EXPORT(T) — лучше всего писать для всех производных
классов, нужен для сериализации через указатель на базовый класс. Иначе
получем exception unregistered class. Смотрите объяснение и подробности.
Также можно напоротся на следующие очевидные грабли: например, если
сериализовал QPoint как запись x и y, а потом пытаешься десериализовать
точку чтением этих x и y из архива, получишь не те результаты, а может и
рантайм эррор — так как пропускаешь чтение информации о типе QPoint.
В заголовке я написал в контексте Qt. Оказывается, что если попытаться сериализовать какой-либо из стандартных классов Qt,
получаем ошибку компиляции — can not access private member T::T, что
означает — коструктор класса объявлен в секции private. Решение проблемы
— перенести макрос Q_DISABLE_COPY в исходниках этого класса из private в
public. Смотрите подробности.
Также сильно раздражает абсурдное следование правилам ООП везде где это
возможно в Qt, а именно: инкапсуляция данных для элементарных классов
(практически структур) — QPointF и QRectF. Если бы я мог обратиться к их
полям, кода получилось бы раз в 5 меньше. Нет, я должен разбивать
функцию serialize на load и save, которые отличаются только тем, что в
одной функции
double x = point.x();
ar & x;
а в другой
double x;
ar & x;
point.setX(x);
В примере выше я не могу написать ar & point.x();
так как почему-то нельзя использовать вычисляемые
выражения (ошибка компиляции), заключение в скобки не помогает. Однако
можно записать выражение вида ar & x & y;
NB: нельзя сериализовать qreal, его надо привести к double, причем таким образом
double x = point.x();
ar & x;
а не
ar & double (point.x());
Дебаг сериализации неприятен
Пример определения сериализации
И применение ее в коде
В целом, результатом остался доволен. Код получился относительно простой
и даже быстрый, вся реализация уместилась в отдельном .h файле.
Сериализация 1 миллиона инстансов производного класса QGraphicsLineItem
прошла за 4,7 секунды (на моем AMD Athlon X2 QL-65) и сгенерировала файл
размером 3.8 мб при сериализации в текстовый поток. Минусы — трудно
дебажить, однако спасает множество исключений на рантайм ошибки. Также
сам код библиотеки boost::serialization снабжен комментариями типа
В интернетах полно негативных отзывов о boost::serialization, кто-то
говорит что она медленная, кто-то недоволен отсутствием контроля версий
класса. Однако это отзывы о старых версиях, в версии boost 1.52.0 (которая использовалась в этом обзоре) есть контроль версий класса и производительность на мой взгляд удовлетворительная.
|