/*!
\page oess_1_2_0__subclassing_by_extension oess_1.2.0. Наследование расширением (subclassing_by_extension)

\section oess_1_2_0__subclassing_by_extension__intro Введение

Механизм \a subclassing_by_extension (наследование расширением)
является дополнением механизма \a расширяемых типов
(см. \ref oess_1_2_0__extensible_types). В частности, subclassing_by_extension
возник для преодоление одной проблемы, которая не решалась с помощью
расширяемых типов, но была очень актуальна для использования
ObjESSty как средства сериализации в организации межпроцессовых
взаимодействий. Ниже подробнее описывается данная проблема и
реализованный в ObjESSty v.1.2.0 способ ее решения.

\section oess_1_2_0__subclassing_by_extension__problem Проблема "неизвестных свойств"

\subsection oess_1_2_0__subclassing_by_extension__problem__inprocess В рамках одного процесса

Предположим, что есть некоторый полезный сервис. Например,
предоставляющий возможность отсылки SMS на мобильный телефон. Этот
сервис реализован в виде C++ библиотеки. Для отсылки SMS в нем
предоставлена функция:
\code
void
send_sms(
	const destination_t & dest,
	const destination_t & src,
	const sms_encoding_t & encoding,
	const std::string & sms_body )
throw( std::exception );
\endcode
Если сервис окажется востребованным, то рано или поздно окажется, что
интерфейс функции send_sms должен быть расширен. Например, для
указания:
- нескольких адресов получателей сообщения;
- приоритета сообщения (если сервис начнет поддерживать
приоритетную обработку запросов);
- политики реакции на отрицательный результат доставки SMS (при большом
трафике исходящих SMS потеря одного сообщения может не считаться
проблемой и в этом случае исключение порождаться не должно);
- указания максимального времени ожидания результата доставки SMS
(в некоторых случаях подтверждение об успешной доставке может
поступать только через 30-60 секунд. При большом трафике исходящих
SMS может быть желательно органичить время ожидания, например, 5
секундами).

Изменение прототипа функции send_sms для удовлетворения возрастающих
потребностей является тупиковым вариантом:
- количество назначаемых дополнительных параметров может достигать
нескольких десятков (причем в различных сочетаниях). Использование же
при программировании функции, в чей прототип входит, например, двадцать
параметров, выглядит нереальным;
- изменение прототипа может быть невозможным, при наличии большого
объема кода, использующего старый прототип. Может оказаться просто
невозможно выполнить изменение этого кода.

Возможность расширения списка параметров функции send_sms можно было
бы осуществить с помощью т.к. карты свойств (traits map):
\code
class	trait_t
{
	public :
		virtual ~trait_t();

		virtual int
		trait_id() const = 0;
};

typedef std::map< int, trait_t * >	trait_map_t;

void
send_sms(
	const destination_t & dest,
	const destination_t & src,
	const sms_encoding_t & encoding,
	const std::string & sms_body,
	const trait_map_t * traits = 0 )
throw( std::exception );
\endcode

Т.е. когда необходимо, скажем, поддержать в send_sms возможность
таймаута ожидания результата доставки SMS и обеспечить приоритетную
обработку, то создаются специальные классы свойств:
\code
class	timeout_trait_t
:	public trait_t
{
	public :
		timeout_trait_t( unsigned int millisec );
		virtual ~timeout_trait_t();

		virtual int
		trait_id() const;

		unsigned int
		timeout() cont;
};

class	priority_trait_t
:	public trait_t
{
	public :
		priority_trait_t( int priority );
		virtual ~priority_trait_t();

		virtual int
		trait_id() const;

		int
		priority() cont;
};
\endcode
которые используются затем, например, так:
\code
trait_map_t traits;

timeout_trait_t timeout( 5000 );
traits[ timeout.trait_id() ] = &timeout;

priority_trait_t priority( -1 );
traits[ priority.trait_id() ] = &priority;

send_sms( dest, src, encoding, sms, &traits );
\endcode

\subsection oess_1_2_0__subclassing_by_extension__problem__interprocess Между процессами

Рассмотрим теперь ситуацию, когда отсылкой SMS занимается серверный
процесс, а инициированием -- клиентский. Для передачи запроса на сервер
необходим какой-то объект, который будет сериализован клиентом и
десериализован сервером. Например, пусть это будет тип send_sms_request_t:
\code
class	send_sms_request_t
:	public oess_1::stdsn::serializable_t
{
	OESS_SERIALIZER( send_sms_request_t )
	private :
		destination_t	m_dest;
		destination_t	m_src;
		sms_encoding_t	m_encoding;
		std::string	m_sms_body;
	...
};
\endcode
который на DDL описывается следующим образом:
\verbatim
{type	send_sms_request_t
	{attr	m_dest	{of destination_t}}
	{attr	m_src	{of destination_t}}
	{attr	m_encoding	{of sms_encoding_t}}
	{attr	m_sms_body	{of std::string}}
}
\endverbatim

Когда придет время расширять функциональность запроса
send_sms_request_t придется учитывать, что клиент и сервер могут
поддерживать разные версии send_sms_request_t. Поэтому решение
с передачей в запросе send_sms_request_t карты свойств в чистом виде
становиться невозможным. Действительно, что произойдет, если
описать send_sms_request_t следующим образом:
\verbatim
{type	trait_t
	{abstract}
}

{type	send_sms_request_t
	{attr	m_dest	{of destination_t}}
	{attr	m_src	{of destination_t}}
	{attr	m_encoding	{of sms_encoding_t}}
	{attr	m_sms_body	{of std::string}}
	{attr	m_traits
		{stl-map {key int}}
		{of {ptr} trait_t}
	}
}
\endverbatim
Если клиент передаст в send_sms_request_t сериализованное представление
неизвестного серверу свойства, то сервер не сможет десериализовать
весь объект send_sms_request_t! Поскольку найдя во входящем потоке
неизвестный объект ObjESSty не сможет определить, где же этот
объект заканчивается, чтобы затем продолжить разбор оставшейся части
потока.

Можно было бы сериализовать объекты по указателям так, чтобы
сохранять размер сериализованного объекта. А затем использовать это
значение для пропуска неизвестных объектов. Но тогда будут просто
проигнорированы критически важные свойства. Например, если рассылка
SMS по нескольким адресам осуществлятся при помощи свойства
group_send_trait_t, то сервер, который не знает про это свойство, должен
отказаться выполнять запрос. Например, пусть базовый класс trait_t
будет не абстрактным, а содержащим атрибут m_must_understand, который
показывает, можно ли игнорировать данное свойство:
\verbatim
{type	trait_t
	{attr	m_must_understand	{of oess_1::int_t}}
}
\endverbatim
Более того, тип trait_t можно даже сделать расширяемым, чтобы в следующих
версиях добавить в него общие для всех свойств атрибуты.

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

Именно эта возможность реализована в механизме subclassing_by_extension.
С его помощью описанная ситуация решается следующим образом:
\verbatim
{type	trait_t
	{extensible}
	{subclassing_by_extension}

	{attr	m_must_understand	{of oess_1::uint_t}}
}

{type	send_sms_request_t
	{attr	m_dest	{of destination_t}}
	{attr	m_src	{of destination_t}}
	{attr	m_encoding	{of sms_encoding_t}}
	{attr	m_sms_body	{of std::string}}

	{attr	m_traits
		{stl-map {key int}}
		{of {extension_of} trait_t}
	}
}

{type	timeout_trait_t
	{extensible}
	{subclassing_by_extension {extension_of trait_t}}

	{attr	m_timeout	{of oess_1::uint_t}}
}

{type	priority_trait_t
	{extensible}
	{subclassing_by_extension {extension_of trait_t}}

	{attr	m_priority	{of oess_1::int_t}}
}
\endverbatim

\section oess_1_2_0__subclassing_by_extension__desciption Описание механизма subclassing_by_extension

\subsection oess_1_2_0__subclassing_by_extension__idea Идея

Для всех типов, которые допускают частичную десериализацию, должен
быть единый корень в иерархии наследования. Именно этот общий
для всех тип должен быть всегда десериализуем. ObjESSty строит
вспомогательный код так, что атрибуты производных типов помещаются
в специальную область расширения базового типа. Тогда десериализующая
сторона извлекает сначала имя типа, который был реально сериализован.
Если этот тип десериализующей стороне известен, то она выполяет
его распаковку обычным образом. В противном случае десериализующая
сторона создает объект базового типа и извлекает в него только содержимое
атрибутов базового типа, а расширение, в котором находятся атрибуты
сериализованного производного класса, пропускается.

Именно из-за того, что атрибуты производных типов сериализуются в
специальное расширение базового типа, данный механизм получил
название subclassing_by_extension.

\subsection oess_1_2_0__subclassing_by_extension__ddl Описание на DDL

На DDL все типы, которые участвуют в механизме subclassing_by_extension,
должны отмечаться тегом <tt>{subclassing_by_extension}</tt>. У типа,
являющегося вершиной иерархии расширяемых таким образом типов,
указывается тег {subclassing_by_extension} без дополнительных
подтегов:
\verbatim
{type	trait_t
	{extensible}
	{subclassing_by_extension}

	{attr	m_must_understand	{of oess_1::uint_t}}
}
\endverbatim
У производных от него типов, в теге {subclassing_by_extension}
должен быть указан подтег <tt>{extension_of}</tt> с именем базового
типа:
\verbatim
{type	timeout_trait_t
	{extensible}
	{subclassing_by_extension {extension_of trait_t}}

	{attr	m_timeout	{of oess_1::uint_t}}
}

{type	priority_trait_t
	{extensible}
	{subclassing_by_extension {extension_of trait_t}}

	{attr	m_priority	{of oess_1::int_t}}
}
\endverbatim

Для указания того, что атрибут является указателем на расширяемый
наследованием тип, должен использоваться тег <tt>{extension_of}</tt>
при указании типа атрибута:
\verbatim
{attr	m_traits
	{stl-map {key int}}
	{of {extension_of} trait_t}
}
\endverbatim
Эта запись соответствует следующей конструкции C++:
\code
std::map< int, trait_t * >	m_traits;
\endcode

\subsection oess_1_2_0__subclassing_by_extension__constraint Ограничения

Механизм subclassing_by_extension требует, чтобы использовалось только
одиночное наследование. Поэтому в теге {subclassing_by_extension}
можно указать только один подтег {extension_of}.

Механизм subclassing_by_extension использует специальный формат
сериализации, который отличается от формата сериализации обычного
наследования. Поэтому, наследование в рамках subclassing_by_extension
указывается с помощью комбинации {subclassing_by_extension {extension_of}},
а не с помощью {super}. В сочетании с возможностью использования
только одиночного наследования это так же означает, что нельзя
одновременно указывать {subclassing_by_extension {extension_of}} и
{super} в описании типа. Между тем, вершина иерархии расширяемых
наследованием типов может быть обычным образом производна от
любых других типов:
\verbatim
{type	A ... }
{type	B ... }
{type	C
	|| Здесь можно использовать обычное наследование.
	{super A}
	{super B}
	{subclassing_by_extension}
	...
}
{type	D
	|| Здесь уже нельзя использовать обычное наследование
	|| и теги {super}.
	{subclassing_by_extension {extension_of C}}
	...
}
\endverbatim

Вершина иерархии расширяемых наследованием типов не может быть
абстрактным типом, т.к. объекты этого типа создаются на десериализующей
стороне, если реальный сериализованный тип этой стороне не известен.

Если какой-то тип обычным образом унаследован от раширяемого
наследованием типа, то объекты этого типа не должны использоваться
в атрибутах {extension_of}. Т.е. следующая конструкция недопустима:
\verbatim
||
|| DDL
||
{type	A {subclassing_by_extension} ... }
{type	B
	{attr a {of {extension_of} A }}
}
{type	C {super A} ...}
\endverbatim
\code
//
// C++
//
B b;
b.a = new C(); // ОШИБКА!
\endcode
К сожалению, на уровне С++ диагностировать эту ошибку нельзя. Она
проявится только при попытке сериализовать объект \a b, т.к. для
обычного и расширяющего наследования используются разные
схемы сериализации.

\subsection oess_1_2_0__subclassing_by_extension__nearest_base Создание объекта ближайшего базового типа при десериализации

При десериализации типа, который не известен десериализирующей стороне
ObjESSty предпринимает попытку создания объекта наиболее близкого
базового типа. Например, если на десериализующей стороне есть
иерархия:
\verbatim
{type	trait_t
	{subclassing_by_extension}
	... }
{type	cipher_t
	{abstract}
	{subclassing_by_extension {extension_of trait_t}}
	... }
{type	symmetric_cipher_t
	{subclassing_by_extension {extension_of cipher_t}}
	... }
{type	tdes_cipher_t
	{subclassing_by_extension {extension_of symmetric_cipher_t}}
	... }
{type	aes_cipher_t
	{subclassing_by_extension {extension_of symmetric_cipher_t}}
	... }

{type	request_t
	...
	{attr	m_traits
		{stl-map {key oess_1::uint_t}}
		{of {extension_of} trait_t}}
}
\endverbatim
а на сериализующей стороне в атрибуте request_t::m_traits был сериализован
объект типа:
\verbatim
{type	blowfish_cipher_t
	{subclassing_by_extension {extension_of symmetric_cipher_t}}
	... }
\endverbatim
то при десериализации будет создан объект типа symmetric_cipher_t, т.к.
в иерархии наследования он расположен ближе к blowfish_cipher_t, чем
тип trait_t.

Для того, чтобы можно было создавать объекты ближайшего базового
типа при десериализации, в сериализованном представлении ObjESSty
сохраняет имена всех типов, которые составляют цепочку наследования.
Т.е. для приведенного примера, в сериализованном представлении
объекта типа blowfish_cipher_t будет сохранен список имен:
{blowfish_cipher_t, symmetric_cipher_t, trait_t}.

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

\subsection oess_1_2_0__subclassing_by_extension__extensible Расширяемость

Типы, входящию в иерархию расширяемых наследованием типов могут
быть "просто расширяемыми" типами. Т.е. в их DDL описании могут
использоваться теги {extensible} и {extension}. Например, пусть первая
версия протокола межпроцессового взаимодействия описывается так:
\verbatim
{type
key_t
	{extensible}

	{attr m_index {of oess_1::uint_t}}
}

{type
value_t
	{extensible}
	{subclassing_by_extension}

	{attr	m_value {of std::string}}
}

{type
data_t

	{attr m_data {of {extension_of} value_t}
		{stl-map {key key_t}}}
}

{type
timeout_value_t
	{extensible}
	{subclassing_by_extension {extension_of value_t}}

	{attr	m_millisec {of oess_1::uint_t}}
}

{type
compression_value_t
	{extensible}
	{subclassing_by_extension {extension_of value_t}}

	{attr	m_preferred {of std::string}}
}
\endverbatim
А вторая версия:
\verbatim
{type
key_t
	{extensible}

	{attr m_index {of oess_1::uint_t}}
}

{type
value_t
	{extensible}
	{subclassing_by_extension}

	{attr	m_value {of std::string}}

	{extension
		{attr m_version {of std::string}
			{default {c++ \"1.0\"}}}
	}
}

{type
data_t

	{attr m_data {of {extension_of} value_t}
		{stl-map {key key_t}}}
}

{type
timeout_value_t
	{extensible}
	{subclassing_by_extension {extension_of value_t}}

	{attr	m_millisec {of oess_1::uint_t}}

	{extension
		{attr	m_period {of oess_1::uint_t}
			{default {c++ 0 }}}
	}
}

{type
compression_value_t
	{extensible}
	{subclassing_by_extension {extension_of value_t}}

	{attr	m_preferred {of std::string}}

	{extension
		{attr	m_level {of oess_1::short_t}
			{default {c++ 9}}}
	}
}

{type
signature_value_t
	{extensible}
	{subclassing_by_extension {extension_of value_t}}

	{attr	m_algorithm {of std::string}}
}
\endverbatim

\subsection oess_1_2_0__subclassing_by_extension__binary Способ сериализации

При использовании двоичной сериализации (единственной доступной
в ObjESSty 1.2.0) объект:
\verbatim
{type	A
	{subclassing_by_extension}

	{attr	a {of oess_1::uint_t}}
	{attr	b {of oess_1::uint_t}}
}
\endverbatim
Будет сериализован в виде следующей псевдо-структуры:
\code
struct	A_bin_image
{
	oess_1::uint_t	a;
	oess_1::uint_t	b;
	oess_1::uint_t	derived_image_size;
	oess_1::char_t	derived_image[ derived_image_size ];
};
\endcode

Производный при помощи расширения тип B:
\verbatim
{type	B
	{subclassing_by_extension {extension_of A}}

	{attr	c {of oess_1::uint_t}
}
\endverbatim
будет представлен псевдо-структурой A_bin_image, у которой в атрибуте
A_bin_image::derived_image будет находится псевдо структура:
\code
struct	B_bin_image
{
	oess_1::uint_t	c;
	oess_1::uint_t	derived_image_size;
	oess_1::char_t	derived_image[ derived_image_size ];
}
\endcode

Соответственно, в B_bin_image::derived_image могут находиться аналогичные
структуры для производных от B классов.

Если же типы A и B являются так же и "просто расширяемыми":
\verbatim
{type	A
	{extensible}
	{subclassing_by_extension}

	{attr	a {of oess_1::uint_t}}
	{attr	b {of oess_1::uint_t}}
}
{type	B
	{extensible}
	{subclassing_by_extension {extension_of A}}

	{attr	c {of oess_1::uint_t}
}
\endverbatim
то псевдо-структуры A_bin_image и B_bin_image имеют вид:
\code
struct	A_bin_image
{
	oess_1::uint_t	a;
	oess_1::uint_t	b;
	oess_1::uint_t	extension_size;
	oess_1::char_t	extension[ extension_size ];
	oess_1::uint_t	derived_image_size;
	oess_1::char_t	derived_image[ derived_image_size ];
};
struct	B_bin_image
{
	oess_1::uint_t	c;
	oess_1::uint_t	extension_size;
	oess_1::char_t	extension[ extension_size ];
	oess_1::uint_t	derived_image_size;
	oess_1::char_t	derived_image[ derived_image_size ];
}
\endcode

*/

