Показаны сообщения с ярлыком Serialization. Показать все сообщения
Показаны сообщения с ярлыком Serialization. Показать все сообщения

понедельник, 2 сентября 2013 г.

[prog.c++] Зафиксировал версию 2.1.0 проекта ObjESSty

Сегодня зафиксировал версию 2.1.0 своей библиотеки для сериализации C++ объектов ObjESSty в виде тега.

Версия 2.1.0 является результатом серьезного рефакторинга версии 2.0, которая в свое время создавалась в условиях жуткого цейтнота и, поэтому, содержала внутри много некрасивостей и недоделок. Версия 2.1.0 привела код ObjESSty в приемлемый для дальнейшего развития вид, новых возможностей в ней почти нет.

На данный момент версия 2.1.0 находится только в SVN-репозитории на SourceForge: tags/oess_2/2.1.0.

Выкладывать архив с ее исходниками я не планировал, т.к. не думаю, что сейчас эта библиотека будет кому-нибудь интересна. Но, если будут пожелания, то опубликую на SF и архив с исходниками, и архив с документаций и все, до чего доберусь ;) Вообще, если будут какие-то вопросы, замечания или предложения, то не стесняйтесь. Высказывайте их здесь, на SourceForge, по почте (eao197 на ГМайле). Все, что способно улучшить ObjESSty будет воспринято мной с большим вниманием и благодарностью.

Сейчас ObjESSty является частью проекта SObjectizer. Недавно на SF переехали все исходники этого проекта и дальнейшая его разработка будет вестись там. Есть у нашей команды задумки, которые мы собираемся воплотить в жизнь. Надеюсь, дело пойдет. Т.к. интерес к агентному программированию (или программированию на акторах) сейчас есть, в наличии хорошие фреймворки для JVM, а вот для С++ ситуация выглядит похуже. Мы хотим это исправить ;) Одна из актуальнейших целей для нас -- это перевод исходников и документации по SObjectizer (и всему, что в него входит) на английский язык. Будем признательны, если найдутся желающие помочь в этой непростой работе.

Лицензия у всех компонентов The SObjectizer Project, кстати говоря, одна из самых вменяемых: 3-х секционная BSD-лицензия. Т.е. бесплатно для любых коммерческих или некоммерческих проектов. Даром, если по простому ;)

Если говорить конкретно об ObjESSty, то на данный момент по скорости она проигрывает Google Protobuf-у где-то в 2.5 раза. Отчасти это объясняется заточенностью под более сложные структуры данных, отчасти несколько менее эффективной реализацией внутри. Так что задачей версии 2.2 будет сокращение отставания по скорости от Protobuf-а до минимума. Так же в версии 2.2 хочется реализовать поддержку таких вещей из C++11, как shared_ptr (и, возможно, weak_ptr), а так же unordered_set/multiset и unordered_map/multimap. А так же попробовать минимизировать объем DDL-описаний, которые начинают раздражать своей многословностью.

Кроме того, в процессе работы над ObjESSty 2.2 хотелось бы определиться с главным акцентом: должна ли ObjESSty развиваться именно как система сериализации сложных структур данных, либо же нужно специализироваться на коммуникационных протоколах (подробнее см. здесь).

В общем, ObjESSty живет и развивается. Кому интересны некоторые продробности, милости прошу под кат.

Уважаемые читатели, если не сложно, то поспособствуйте распространению этой заметки. Например, нажимая +1 в Google+. Большое спасибо за понимание!

пятница, 30 августа 2013 г.

[prog.thougts] Для разных целей разная сериализация...

Я уже давно имею дело с сериализацией различных структур данных в C++. Началось это где-то в 1995-м, когда я предпринял попытку создания объектной СУБД. Объектная СУБД -- это такая штука, в которой объекты хранятся как есть, без расщепления на составляющие. Например, если мы попытаемся сохранить в реляционной СУБД C++ный объект, который содержит не только простые атрибуты (int-ы или string-и), но и агрегаты (например, вектора или множества строк), то нам потребуется в схеме данных БД предусмотреть дополнительные таблицы для хранения значений этих агрегатов. А при сохранении объекта проводить раскидывание значений его атрибутов по разным таблицам (собственные атрибуты в одну таблицу, атрибуты из объектов внутри агрегата -- в другую). Аналогично и при чтении объекта из БД. В свое время евангелисты ООСУБД проводили такую аналогию: хранение объектов в РСУБД похоже на хранение автомобиля в гараже в полностью разобранном виде :)

Различные системы объектно-реляционного мэппинга (ORM) могут скрывать от разработчика всю трудоемкость этих преобразований объектной модели в реляционную, но, тем не менее, разработчик должен понимать, хотя бы в общих чертах, что именно происходит и чем ему за это придется платить. А вот при работе с ООСУБД такого расщепления объекта на составляющие не происходит, сама СУБД отвечает за то, чтобы из сложного объекта получить один сериализованный образ и сохранить его в БД. Поэтому продолжая аналогию с автомобилем, в ООСУБД автомобиль помещается в гараж и извлекается из гаража в полностью собранном виде, не тратя время на сборку/разборку. В определенных прикладных областях подход ООСУБД оказывался намного эффективнее (например, при работе с векторными изображениями, где объектами являются элементы изображения).

Так вот, занимаясь объектной СУБД пришлось решать задачу сериализации сложных C++ных объектов без участия разработчика. Под сложными я подразумеваю объекты, которые состоят не только из значений примитивных типов (т.к. int, float/double, char[]/string). В качестве типов атрибутов могут указываться другие сериализуемые пользовательские типы. А так же агрегаты (простые C-шные вектора, STL-контейнеры), элементами которых могут быть как примитивные, так и сложные пользовательские типы. Кроме того, поскольку речь идет об объектах, обязательно должно поддерживаться наследование. А уж коль речь зашла о C++, то и множественное наследование, ну и раз множественное, то и виртуальное множественное. Например, в следующем псевдокоде класс Polygon является сложным объектом (в отличии, например, от Color или Point):

struct Color {
   int8_t r;
   int8_t g;
   int8_t b;
};

struct Point {
   float x;
   float y;
};

struct Polygoin : public ImageItem {
   std::vector< Point > points;
   Color border_color;
   Color fill_color;
   int32_t fill_pattern_id;
};

Под "сериализацией без участия разработчика" я подразумеваю то, что разработчику не нужно писать код, управляющий (де)сериализацией. Т.е. программист не должен вручную писать что-то вроде:

Archive &
operator<<( Archive & a, const Polygon & o )
{
   a << o.points.size();
   forauto & p : o.points )
      a << p;

   a << o.border_color
      << o.fill_color
      << o.fill_pattern_id;

   return a;
}

Archive &
operator>>( Archive & a, Polygon & o )
{
   std::size_t points_count;
   a >> points_count;
   while( points_count-- )
   {
      Point p;
      a >> p;
      o.points.push_back( p );
   }

   a >> o.border_color
      >> o.fill_color
      >> o.fill_pattern_id;

   return a;
}

Применительно к объектным СУБД возможность (де)сериализации без участия программиста была нужна вовсе не для того, чтобы упростить жизнь пользователю СУБД. Там это было необходимо для того, чтобы СУБД могла получать доступ к значениям атрибутов объекта (для построения индексов, проведения поисковых запросов, просмотра/модификации данных из административных GUI-инструментов) и для поддержки эволюции схемы данных (например, для автоматической конвертации значений объектов при изменении типа Point::x/y с float на double). Но, на мой взгляд, именно автоматическое управление (де)сериализацией объектов делает практически осуществимой работу с действительно сложными структурами данных.

Мой первый опыт работы с автоматической (де)сериализацией C++ных структур данных был накоплен в период с 1995-го по 2000-й год во время попытки создать полноценную клиент-серверную объектно-ориентированную СУБД. Через два года, в 2002-м этот опыт был использован в новой разработке ObjESSty, которая задумывалась как a) встраиваемая в приложения система долговременного хранения объектных данных + b) система (де)сериализации этих самых объектных данных.

Спустя 10 лет от поддержки в ObjESSty встраиваемого долговременного хранилища я решил оказаться. А вот в качестве системы (де)сериализации данных ObjESSty продолжает развиваться и сейчас. Как раз длительный опыт использования ObjESSty в разных качествах привел меня к мысли, что не смотря на то, что одна и та же сериализация может использоваться для разных целей, все-таки есть важные отличия между двумя основными задачами сериализации данных:

  • для долговременного хранения и последующего чтения/воспроизведения/модификации информации. Например, бинарный Word-овский doc-файл, который может внутри себя содержать текст, картинки, таблицы и пр. Или sav-файл компьютерной игры, в котором сохраняется текущее состояние игрового процесса;
  • для поддержки транспортных протоколов (коммуникация между взаимодействующими между собой приложениями/узлами сети). Например, текстовый UCP/EMI. Или бинарный SMPP. Или же текстовый SOAP.

Фокус в том, что универсальная система (де)сериализации может применяться и для того, и для другого. Например, XML-представление может использоваться как для долговременного хранения данных (см., например, Open Document Format), так и в качестве формата транспортного протокола (SOAP), хотя за применение XML в транспортном протоколе нужно изгонять из профессии ;) Поэтому такое различие несколько условно. Между тем, есть моменты, которые показывают, что дьявол таки скрывается в деталях.

Первый момент -- это вопрос сложности поддерживаемых системой (де)сериализации структур данных. Если речь идет о транспортном протоколе, то вряд ли имеет смысл выходить за рамки простого использования объектов в качестве атрибутов других объектов и агрегатов сложнее векторов однотипных объектов. Нужна ли, например, в транспортном протоколе поддержка multimap-ов? Или наследования объектов (и уж тем более множественного наследования)? Это, конечно, зависит от протокола, но, скорее всего, вряд ли. Однако, если речь идет о долговременном хранении сложных данных, то поддержка основных структур данных языка и наследования должны быть обязательно, иначе разработчики придется слишком много возится с преобразованием данных вручную. Так же крайне полезным будет поддержка атрибутов-указателей, причем указателей на полиморфные объекты (т.е. атрибут имеет тип указателя на базовый класс, а содержать будет указатели на объекты производных классов). А так же, в некоторых случаях, окажется востребованной и поддержка циклических ссылок между объектами.

Второй момент -- это сложность формата сериализованного представления данных. С ростом вычислительной мощности обычных компьютеров, объемов доступной оперативной памяти и размеров жестких дисков этот вопрос для долговременного хранения данных все менее и менее важен. Разработчики офисных пакетов могут себе позволить применять XML, который затем будет еще сжат zip-ом, не сильно считаясь с расходами вычислительной мощности, оперативной памяти и диска. А вот разработчики транспортного протокола могут быть вынуждены уделять вопросам сложности формата данных самое серьезное внимание. Например, если одна из взаимодействующих сторон -- это маломощное встроенное устройство, программу для которого нужно писать учитывая каждый байт и контролируя глубину стека. Парсить в таких условиях XML или даже JSON не очень разумно. Или если транспортный протокол разрабатывается для мощных серверов, которые обмениваются друг с другом сообщениями в темпе 15000 в секунду.

Интересная сторона сложности формата -- это ручной его разбор. В случае долговременного хранения информации разработчикам не так уж часто приходится вручную разбирать сериализованный образ объекта, чтобы понять где и что лежит. Как правило, этим приходится заниматься разработчикам самой системы сериализации, но не ее пользователям. А вот в случае транспортного протокола ситуация меняется. Пользователям системы сериализации довольно часто бывает необходимо вручную расшифровать полученный от кого-то дамп коммуникационного обмена. И если система сериализации как-то хитро упаковывает объекты, то эта задача может оказаться еще той работенкой. Например, очень неприятно разбирать дампы двоичных протоколов, в которых информация передается в Little Endian представлении. Особенно когда там много 32-х или 64-х битовых целочисленных значений :)

Третий момент, плотно взаимосвязанный со вторым, -- это объем сериализованного представления данных. В общем случае транспортные протоколы обычно требовательнее к объемам, чем долговременное хранение данных. Но тут не так все очевидно. Например, если для долговременного хранения сериализуются объемные данные (видео, аудио, сложные векторные изображения, результаты интенсивных измерений), то объем их сериализованного представления, а так же скорость их (де)сериализации могут оказаться критичнее, чем объемы единичных пакетов какого-нибудь транспортного протокола, вроде SMPP. С другой стороны для некоторых транспортных протоколов могут существовать специфические ограничения на формат данных. Например, если протокол используется для взаимодействия устройств по 32-х битовой транспортной шине, где передача одного двойного слова (4 байта) осуществляется за один такт, очень желательно размещать поля сериализуемого объекта на границе 4-х байт. Поэтому, если поле A имеет размер шесть байт, а следующее за ним поле B -- три байта, то для двоичного представления полей A и B потребуется 12 байт: первые 8 для поля A (из которых два последних будут пустыми, используемыми только для выравнивания -- т.н. padding bytes), затем 4 для поля B (где первые 3 содержат значение B, а четверый -- это padding byte).

Четвертый момент, плотно связанный с третьим, -- это минимизация объема сериализованного представления за счет опциональных значений атрибутов. Представьте себе, что у вас в программе у множества объектов есть пара-тройка атрибутов, имеющих значение по умолчанию. Например, пусть у вас есть объект PhotoDescription с атрибутами title, subject, rating, tags, comments, authors, copyright и т.д. В ряде случаев для большинства объектов PhotoDescription эти поля будут иметь пустые значения. Нужно ли их сериализовать? Ведь это и объем, и вычислительные ресурсы. Допустим, что сохранять опциональные значения не нужно. Как это будет выглядеть в сериализованном представлении объекта? Например, если используется структурированный текстовый формат (XML, JSON) можно не указывать соответствующие теги. Если же используется двоичный формат, как указывать опциональность атрибутов? Используется ли при этом теговое представление данных или нет? Можно ли использовать битовые флаги?

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

Шестой момент, плавно проистекающий из пятого, -- это возможность модификации структуры сериализуемых данных, так же известная как полноценная поддержка эволюции схемы данных. И здесь, как правило, имея дело с транспортными протоколами, не приходится сталкиваться с кардинальными изменениями схемы данных. Например, если в первой версии протокола координаты передавались в виде float-ов, то крайне тяжело будет изменить float на double. Тогда как если координаты сохранялись в файле с векторным изображением, то следующая версия запросто может потребовать на лету преобразовать старые значения из float-а в double, а потом выполнить обратное преобразование, если происходит сохранение в режиме совместимости с предыдущими версиями.

Седьмой момент -- это поддержка межъязыковой и межплатформенной интероперабильности. В случае с транспортным протоколом очень велика вероятность, что одна из взаимодействующих сторон будет написана совсем на другом языке программирования. Не на C++, к примеру, а на Java, C#, Python или Ruby. И работать она будет совсем на другой аппаратной платформе. Поэтому для транспортных протоколов очень желательно, чтобы выбранная система (де)сериализации поддерживала несколько разных языков программирования и не была привязана к конкретной аппаратной платформе. Для долговременного хранения информации это не так актуально, но наличие такой интероперабильности так же будет большим плюсом.

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

По хорошему, об этом нужно думать всем: и пользователям систем сериализации, и их разработчикам. Но с пользователями особенно-то ничего не сделаешь. В простейших случаях они вообще не думают ;) Ну а чего думать -- есть XML, его и возьмем. Ну не нравится XML, возьмем JSON. Когда повезло настолько, что уперлись в производительность/доступные объемы, тогда уже можно будет посмотреть на что-нибудь более шустрое и компактное: ASN.1, Google Protobuf, Apache Thrift. Ну а тех, кому реально приходится работать с большими и сложными структурами данных, похоже, не так уж и много. Тем не менее, полезно задумываться о том, а заточена ли выбранная вами система сериализации под те нужды, для которых вы ее выбрали. Скажем, Google Protobuf вполне сгодится для создания транспорта. Но вряд ли стоит брать его для хранения sav-файлов в RTS-игре с большим количеством разнообразных юнитов. Здесь Apache Thrift подошел бы получше. А ObjESSty еще лучше ;)

А вот разработчикам систем сериализации думать о том, какую именно задачу решает их система, нужно очень и очень серьезно. Транспорт -- это одно. Здесь акценты на скорости, простоте и компактности представления, своеобразных путях расширения схемы данных. Долговременное хранение информации -- это совсем другое. Хотя, опять же, сильно может зависеть от характера информации. Хранить результаты интенсивных измерений (огромное количество в принципе однотипных значений, возможно каким-то образом сгруппированных) -- это одно. Текстовый или графический документ, или чертеж из САПР -- это совсем другое.

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

Но теперь, по прошествии более десяти лет использования и развития ObjESSty, приходится всерьез задумываться о том, на чем же именно сделать акцент. И вопрос этот непростой. Отчасти потому, что ObjESSty успешно зарекомендовала себя при организации транспорта. Но здесь сильная конкуренция со стороны тех же Protobuf, Thrift, XML и JSON. С другой стороны, не так уж много для C++ готовых инструментов для сериализации сложных структур данных. И это можно было бы обратить в свою пользу. Так что есть о чем подумать.


Еще на эту тему:

среда, 4 ноября 2009 г.

[comp.prog.thoughts] Почему мне не нравится подход Boost.Serialization

Нашлось время чтобы еще раз перечитать документацию по Boost.Serialization, поэтому выполняю свое давнее обещание рассказать о том, почему я не считаю подход Boost-а к сериализации правильным.

Причина проста – в Boost.Serialization программист сам пишет код, декларирующий сериализацию/десериализацию, и в этом коде возможности для оформления каких-то метаописаний сильно ограничены.

Например, решил разработчик использовать простой текстовый/бинарный архив и написал код:

class gps_position
{
    friend class boost::serialization::access;
    friend std::ostream & operator<<(std::ostream &os, const gps_position &gp);

    int degrees;
    int minutes;
    float seconds;

    template< class archive >
    void serialize(Archive & ar, const unsigned int /* file_version */){
        ar  & degrees
            & minutes
            & seconds;
    }
    ...

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

template< class archive >
void serialize(Archive & ar, const unsigned int /* file_version */){
    ar  & BOOST_SERIALIZATION_NVP(degrees)
        & BOOST_SERIALIZATION_NVP(minutes)
        & BOOST_SERIALIZATION_NVP(seconds);
}

Теперь он может сериализовать данные как в простую, так и в XML-форму. Но что ему делать, если со временем ему потребуется еще и сериализация на основе TLV (Tag-Length-Value)? А если ему затем захочется делать опциональные атрибуты (т.е. такие, которые присутствуют только при выполнении каких-то условий)? Если ему потребуется в каком-то архиве хранить uint32-поле в четырехбайтовом представлении, а в другом архиве – в компактном (чтобы, скажем, значение 12 хранилось с помощью всего одного байта)? В конце-концов, что ему делать, если бинарный архив потребуется прочитать из программы на другом языке?

На мой очень предвзятый взгляд все это решается гораздо проще при наличии метаописаний, из которых специальными трансляторами генерируется вспомогательный код для сериализации/десериализации.

Для демонстрации своей мысли я буду использовать синтаксис метаописаний своей библиотеки сериализации ObjESSty, т.к. его я знаю хорошо (в отличии от ASN.1, Google Protocol Buffers, Facebook Thrift).

Итак, пусть все начинается с простого случая – бинарной сериализации gps_position. Для нее описание данных будет иметь вид:

{type gps_position
  {attr degrees {of oess_1::int_t}}
  {attr minutes {of oess_1::int_t}}
  {attr seconds {of oess_1::single_t}}
}

Если возникает потребность использовать XML-сериализацию так, чтобы имена XML-тегов совпадали с именами атрибутов, то ничего больше изменять не нужно. Поскольку транслятор DDL-описания и так знает имена атрибутов. Если же нужно использовать для атрибутов minutes и seconds имена min и sec, то это описывается, скажем, так:

{type gps_position
  {attr degrees {of oess_1::int_t}}
  {attr minutes {of oess_1::int_t} {xml {element "min"}}}
  {attr seconds {of oess_1::single_t} {xml {element "sec"}}}
}

Если затем возникает необходимость в TLV-сериализации, то это так же описывается в DDL:

{type gps_position
  {attr degrees {of oess_1::int_t}
    {tlv {tag 0x01}}
  }
  {attr minutes {of oess_1::int_t}
    {xml {element "min"}}
    {tlv {tag 0x02}}
  }
  {attr seconds {of oess_1::single_t}
    {xml {element "sec"}}
    {tlv {tag 0x03}}
  }
}

С XML-элементами и TLV-тегами может произойти неприятная ситуация: со временем имена элементов и значения тегов могут меняться, но нужно будет читать и старые архивы. Поэтому при десериализации нужно будет уметь распознавать несколько имен/тегов. В DDL-описании это сделать не сложно:

{type gps_position
  {attr degrees {of oess_1::int_t}
    {xml {element "d"}}
    {tlv {tag 0x51} {compat-tag 0x01}}
  }
  {attr minutes {of oess_1::int_t}
    {xml {element "m"} {compat-element "min"}}
    {tlv {tag 0x52} {compat-tag 0x02}}
  }
  {attr seconds {of oess_1::single_t}
    {xml {element "s"} {compat-element "sec"}}
    {tlv {tag 0x53} {compat-tag 0x03}}
  }
}

Далее, пусть потребовалось для бинарных архивов одного типа хранить атрибуты degrees и minutes в компактном виде, а для бинарных архивов другого типа – в четырехбайтовом представлении, чтобы экономить время распаковки. В DDL-описании можно оформить и такие условия:

{type gps_position
  {attr degrees {of oess_1::int_t}
    {xml {element "d"}}
    {tlv {tag 0x51} {compat-tag 0x01}
      {default-image-size compact}
      {archive-type "max-speed" {image-size 32bit}}
    }
  }
  ...
}

Все эти описания могут быть автоматически преобразованы транслятором DDL во вспомогательный код на разных языках, что позволит, например, писать архив в C++ программе, а читать его в Java-программе.

Отдельный вопрос с опциональными атрибутами, т.е. атрибутами, которые не должны попадать в архив, если они имеют какое-то стандартное значение. Например, пусть есть какая-то структура, описывающая параметры компрессии данных (имя алгоритма компрессии, номер версии алгоритма, степень сжатия, размер словаря данных и пр.). Пусть в большинстве случаев компрессия вообще не используется, т.е. все элементы этой структуры нулевые. Смысла сохранять их в архиве нет. Как обрабатывать такие атрибуты?

Общий принцип сериализации таких атрибутов в бинарное представление состоит в том, что в архив помещается флаг, который показывает наличие/отсутствие атрибута (для TLV- или XML-представления ситуация проще). Данный флаг может сохраняться в архиве несколькими способами: в виде отдельного бита в “большой” битовой маске или в виде байта/бита, предшествующего значению атрибута. Конкретный способ – это делали работы архива, программист не должен об этом задумываться. О чем должен думать программист – это о том, чтобы объявить каким-то способом атрибут опциональным. В случае с внешним метаописанием это не сложно:

// Подлежащие сериализации C++ классы.
class compression_info_t
  {
  ...
  public :
    bool is_default() const { ... }
    static compression_info_t default_value() { ... }
    ...
  };
class data_package_t
  {
  ...
    compression_info_t m_compression_info;
  };

// Метаописание.
[type deta_package_t
  ...
  {attr m_compression_info {of compression_info_t}
    {default
      || Значение, которое должен получить атрибут при
      || десериализации, если он не был сериализован.
      {c++ compression_info_t::default_value() }

      || Логическое условие, которое указывает, будет ли
      || атрибут сериализоваться.
      {present_if {c++ !m_compression_info.is_default() }}
    }
  }
}

Благодаря такому метаописанию вспомогательный код по сериализации объекта сможет сам проверить, подлежит ли атрибут сериализации и выставит флаг наличия атрибута в архиве. А при десериализации назначит нужное значение атрибуту, который отсутствовал в архиве.

Как тоже самое сделать для текстовых и бинарных архивов в Boost.Serialization – я не очень представляю. Можно, например, вручную управлять флагами:

class data_package_t
  {
    friend class boost::serialization::access;
    BOOST_SERIALIZATION_SPLIT_MEMBER()
// Способ первый: общая битовая маска.
    template< class Archive >
    void save( Archive & ar, const unsigned int version ) const
      {
        bit_mask_t opt_flags;
        if( !m_compression_info.is_default() )
          opt_flags.set_bit( COMPRESSION_INFO );
        ...
        ar & opt_flags;
        ...
        if( opt_flags.is_bit_set( COMPRESSION_INFO ) )
          ar & m_compression_info;
        ...
      }
    template< class Archive >
    void load( Archive & ar, const unsigned int version )
      {
        bit_mask_t opt_flags;
        ar & opt_flags;
        ...
        if( opt_flags.is_bit_set( COMPRESSION_INFO ) )
          ar & m_compression_info;
        else
          m_compression_info = compression_info_t::default_value();
        ...
      }

// Способ второй: булевое поле, предшествующее атрибуту.
    template< class Archive >
    void save( Archive & ar, const unsigned int version ) const
      {
          if( !m_compression_info.is_default() )
            {
              ar & true;
              ar & m_compression_info;
            }
          else
            ar & false;
        ...
      }
    template< class Archive >
    void load( Archive & ar, const unsigned int version )
      {
        bool compression_info_present;
        ar & compression_info_present;
        if( compression_info_present )
          ar & m_compression_info;
        else
          m_compression_info = compression_info_t::default_value();
        ...
      }

Но, во-первых, я не уверен, что для всех типов архивов Boost.Serialization гарантирует запись/чтение значения сразу после выполнения operator&() (ведь для XML-архивов порядок следования атрибутов может быть произвольным). И, во-вторых, как только программист начинает писать подобный код, работа с разными типами архивов (текстовыми, бинарными, XML, TLV) сразу же превращается в т.н. hardcoding. Тогда как в случае с метаописанием всеми этими деталями занимается не разработчик, а транслятор метаописания во вспомогательный код.

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

PS. В ObjESSty нет поддержки TLV- и XML-сериализации. XML-формат никогда не был мне нужен, а TLV-сериализация однажды понадобилась. Упомянутые выше формы описания TLV-тегов в DDL как раз тогда и рассматривались. Развития эта идея пока не получила, т.к. оказалось проще сделать поддержку TLV для десятка атрибутов вручную, чем модифицировать ObjESSty.