oess_1: Язык ObjESSty DDL

Назначение

Язык ObjESSty Data Definition Language (DDL) предназначен для описания сериализуемых типов. Такие типы также называют устойчивыми, долговременными или сохраняемыми (persistent). В свою очередь, сохраняемый тип состоит из сохраняемых атрибутов. Не обязательно, чтобы все атрибуты сохраняемого типа были сохраняемыми. Могут быть случаи, когда в типе нужно сохранять всего один атрибут из числа всех его атрибутов. Могут быть так же случаи, когда у типа нет ни одного сохраняемого атрибута -- это означает, что тип будет использоваться в качестве вершины иерархии сохраняемых типов. Атрибуты, которые не сериализуются, называются несохраняемыми атрибутами (transient).

Общие сведения о формате DDL

За основу синаксиса DDL взят синтаксис языка программирования Curl (http://www.curl.com). Поддержка DDL реализована с использованием библиотеки Curl Like Syntax (cls_2, http://eao197.narod.ru/projects.htm#cls_2), поэтому иногда употребляются термины cls_2-синтаксис или cls_2-формат.

Особенности синтаксиса DDL

DDL описание состоит из тегов. Каждый тег может содержать токены и/или дочерние теги. Каждый тег начинается с открывающей фигурной скобки, за которой без пробелов следует название тега. Название может состоять из любых непробельных символов.

DDL описание может содержать комментарии, которые полностью игнорируются при разборе. Существуют два вида коментариев:

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

При разброре многострочные комментарии заменяются на один пробельный символ. Однострочные комментации заменяются на один символ перевода строки.

Значениями тега называются все токены, указанные внутри тега без учета дочерных тегов. Различаются три типа токенов:

Разница между nonspace и string заключается в том, что внутри строки могут содержаться пробелы. Поэтому строка должна обязательно начинаться с двойной ковычки и закрываться двойной кавычкой.

Символы {, }, |, \, " являются управляющими. Поэтому их нельзя использовать непосредственно в токенах nonspace и string. При необходимости использования этих значений необходим префиксовать их с помощью обратного слеша. Например:

Для того, чтобы двойная кавычка не открывала токен типа string достаточно записать ее в виде escape-последовательности.

Примечание. Указанные выше управляющие символы обязательно должны быть представлены в виде escape-последовательностей внутри токена типа string.

Для записи любого символа можно использовать целочисленное значение кода символа, представленное в одной из систем счисления:

Так же поддерживаются следующие escape-последовательности:

DDL описание схемы данных

DDL описание схемы данных представляет из себя текстовый файл, в котором с помощью тегов {type} перечислены все сохраняемые типы. Не обязательно помещать описание всей схемы данных всего приложения в один ddl-файл. Не обязательно так же размещать в одном ddl-файле описания всех типов, которые как-либо используют друг-друга. Если при обработке DDL описания ObjESSty встречает неизвестное имя типа, то это не считается ошибкой. В этом случае ObjESSty предполагает, что данный тип будет описан в другом ddl-описании.

Основное назначение DDL-описания -- это генерация по нему вспомогательного кода для стандартной схемы сериализации (см. oess_1::stdsn). Поэтому важно, чтобы все типы были определены должным образом на уровне C++ описаний и кода, а не на уровне DDL-описания. Так, когда ObjESSty встречает имя типа, ObjESSty использует это имя затем в сгенерированном C++ коде. Если при компиляции этого кода тип оказывается неизвестным, то тогда программит должен определить, что произошло. Если он указан неправильное имя типа в DDL описании, то следует поправить DDL-описание. Если же имя типа правильное, то программист должен подключить к вспомогательному коду все необходимые заголовочные файлы.

Тег {type}

Каждый сохраняемый тип описывается тегом {type}, который имеет вид:

{type <имя>
	[{abstract}]
	[{extensible}]
	[{subclassing_by_extension ...]}]
	[{super ...}]*
	[{attr ...}]*
	[{extension ...}]
	[{cpp-mapping ...}]
}

<имя> определяет имя типа в схеме данных. Оно не должно содержать пробелов. Имя типа в схеме данных может отличаться от имени типа в С++. В этом случае для генерации правильного вспомогательного кода реальное C++ имя типа нужно указать в теге {cpp-mapping {name}} (Тег {type {cpp-mapping {name}}}).

Тег {abstract} должен быть указан, если С++ тип является абстрактным. Этот тег предписывает ObjESSty не генерировать во вспомогательном коде инструкций по инстанциированию объектов этого типа. Объявленные абстрактными типы не могут затем использоваться в качестве типов атрибутов-значений. Но они могут использоваться в качестве типов атрибутов-указателей.

Тег {extensible} указывает, что тип является расширяемым и для него может использоваться тег {extension}. Подробнее эти теги описанны в Описание расширяемых типов на DDL.

Тег {subclassing_by_extension} используется для задействования механизма subclassing by extension. Подробнее см. oess_1.2.0. Наследование расширением (subclassing_by_extension).

Тег {super} используется для перечисления базовых типов. Подробнее см. Тег {type {super}}.

Каждый тег {attr} должен описывать один сохраняемый атрибут. Подробнее формат тега {attr} описан в Тег {type {attr}}.

Тег {cpp-mapping} описывает дополнительные параметры для отображения типа в C++. Подробнее этот тег описан в Тег {type {cpp-mapping}}.

Все вышеперечисленные теги являются необязательными. Обязательным является только имя типа.

Если C++ тип входит в какое-либо пространство имен, то его имя в схеме данных должно быть либо полным именем типа (с учетом всех пространств имен), либо должен использоваться тег {cpp_mapping {type}}:

namespace oess_1 {
namespace db {
namespace storage {
namespace impl {

class stream_storage_dir_item_t
  : public oess_1::stdsn::serializable_t
  { ... }; 
...

class stream_storage_dir_t
  : public oess_1::stdsn::serializable_t
  { ... };

} /* namespace impl */
} /* namespace storage */
} /* namespace db */
} /* namespace oess_1 */

{type	oess_1::db::storage::impl::stream_storage_dir_item_t
	...
}

{type	oess_1::db::storage::impl::stream_storage_dir_t
	...
}

Порядок следования тегов важен только для однотипных тегов. Так, важен взаимный порядок следования тегов {super} и {attr}: в каком порядке они указанны, в таком же порядке будет осуществляться сериализация базовых типов и атрибутов.

Тег {type {super}}

Для каждого базового типа в описании сохраняемого типа должен быть указан свой тег {super}, который имеет формат:

{super [{virtual}] [{public}] <имя>}

<имя> -- это имя базового типа в схеме данных (в C++ базовый тип может иметь другое имя, см. Тег {type {cpp-mapping {name}}}).

Тег {virtual} должен быть указан, если используется виртуальное наследование.

Тег {public} зарезервирован. В настоящее время он не обрабатывается и указывать его в описании не рекомендуется.

Тег {super} не должен использоваться, если тип унаследован непосредственно от oess_1::stdsn::serializable_t.

Пример 1.
Следующему С++ типу:
class my_derived_t : public my_base_t
  { ... };
должно соответствовать следующее описание:
{type	my_derived_t
	{super my_base_t}
	...
}
Пример 2.
Следующему C++ типу:
class my_derived_t : public virtual my_base_t
  { ... };
должно соответствовать описание:
{type	my_derived_t
	{super {virtual} my_base_t}
	...
}
Пример 3.
Следующему C++ типу:
class my_derived_t
  : public my_base_t
  , protected my_protected_base_t
  , private my_private_base_t
  { ... };
должно соответствовать описание:
{type	my_derived_t
	{super my_base_t}
	{super my_protected_base_t}
	{super my_private_base_t}
	...
}

Тег {type {attr}}

Для каждого сохраняемого атрибута в описании типа должен быть указан тег {attr}, который имеет формат:

{attr <имя>
	[<тип контейнера>]
	{of [{ptr} | {extension_of}] <имя типа>}
	[{default {c++ <выражение 1>}
		[{present_if {c++ <выражение 2>}]
	}]
}

<имя> -- это имя сериализуемого атрибута. Должно использоваться тоже самое имя, что и в C++ описании типа.

Тег {of} задает тип атрибута. В качестве <имя типа> могут использоваться либо имена других сериализуемых типов, либо имена встроенных типов ObjESSty:

Тег {of {ptr}} указывает, что типом является указатель на указанный тип. Т.е. запись:

{attr a {of {ptr} A}}
соответствует:
A * m_a;

Тег {of {extension_of}} указывает, что типом является указатель на тип, который использует механизм subclassing by extension (см. oess_1.2.0. Наследование расширением (subclassing_by_extension)).

Если атрибут является контейнером, то тип контейнера должен задаваться одним из следующих тегов:

Заметки:
Для STL контейнеров должны использоваться предикаты сравнения и аллокаторы по-умолчанию. Т.е. нельзя сериализовать std::set у которого используется предика сравнения, отличный от std::less, или std::vector, у которого используется собственный аллокатор.
Тег {default} задает значение атрибута по-умолчанию. Данный тег нужно использовать, если атрибут является опциональным (см. oess_1.2.0. Опциональные поля) или входит в расширение типа (см. oess_1.2.0. Расширяемые типы).

Пример 1.
C++ типу:
class my_type_t : public oess_1::stdsn::serializable_t
  {
    OESS_SERIALIZER( my_type_t )
  private :
    enum { data_capacity = 64 };

    int m_i;
    float m_f;
    char  m_data[ data_capacity ];
  ...
  };
должно соответствовать DDL описание:
{type	my_type_t
	{attr m_i {of oess_1::int_t}}
	{attr m_f {of oess_1::single_t}}
	{attr m_data
		|| DDL ничего не знает про C++ константы, поэтому
		|| размерность вектора должна быть указана явно.
		{fixed-vector 64}
		{of oess_1::char_t}}
}
Пример 2.
Следующему C++ описанию:
class event_key_t : public oess_1::stdsn::serializable_t
  {
    OESS_SERIALIZER( event_key_t )
  private :
    std::string m_event_class;
    oess_1::uint_t  m_event_priority;
    ...
  public :
    ...
    bool
    operator<( const my_key_t & o ) const;
    ...
};
class event_data_t : public oess_1::stdsn::serializable_t
  {
    OESS_SERIALIZER( event_data_t )
  private :
    std::multiset< oess_1::uint_t > m_tickets;
    ...
  };
class dispatcher_t : public oess_1::stdsn::serializable_t
  {
    OESS_SERIALIZER( dispatcher_t )
  private :
    std::list< std::string >  m_event_sources;
    std::map< event_key_t, event_data_t > m_pending_events;
    ...
  };
должно соответствовать DDL описание:
{type	event_key_t
	{attr m_event_class {of std::string}}
	{attr m_event_priority {of oess_1::uint_t}}
}
{type	event_data_t
	{attr m_tickets {stl-set {multi}} {of oess_1::uint_t}}
}
{type	dispatcher_t
	{attr m_event_sources {stl-list} {of std::string}}
	{attr m_pending_events
		{stl-map {key event_key_t}}
		{of event_data_t}}
}

Тег {type {cpp-mapping}}

Тег {cpp-mapping} предназначен для указания генератору вспомогательного кода дополнительной информации о том, как сериализуемый тип описан в C++. Тег имеет формат:

{cpp-mapping [{name <имя типа>}]}

Тег {type {cpp-mapping {name}}}

Тег {cpp-mapping {name}} задает реальное имя сериализуемого типа в C++. Это может потребоваться, если по каким-либо причинам имя типа в DDL не совпадает с именем типа в C++. Например, из-за эволюции схемы данных.

Если в {cpp-mapping {name}} задано имя C++ типа, то это имя используется для генерации вспомогательного кода.

Пример.
Пусть изначально сериализуемый тип не принадлежал никакому пространству имен:
{type	event_dispatcher_info_t
	...
}
Но с развитием проекта возникла потребность перенести event_dispatcher_info_t в специальное пространство имен scheduling::core. Для того, чтобы не потерять совместимости с уже сериализованными данными (в которых может быть зафиксированно имя event_dispatcher_info_t, а не scheduling::core::event_dispatcher_info_t), в DDL имя типа сохраняется, но указывается, что в C++ этот тип имеет другое имя:
{type event_dispatcher_info_t
	...
	{cpp-mapping
		{name scheduling::core::event_dispatcher_info_t}}
}

Документация по ObjESSty. Последние изменения: Fri Oct 13 18:35:37 2006. Создано системой  doxygen 1.4.7
Hosted by uCoz