namespace oess_1
{

/*!
	\brief Компонент %stdsn.

	Данный компонент содержит основные классы и определения для
	стандарного способа сериализации сущностей.

Реализованный в данном компоненте способ сериализации называется стандартным
(штатным) потому, что только этот способ сериализации может использоваться
для сохранения объектов в oess_1::db.

Идея этого способа состоит в том, что по специальному описанию на языке
DDL (подробнее см. oess_1::scheme) для сериализации/десериализации объектов
строится вспомогательный код. Когда объект сериализуется ObjESSty использует
этот код для преобразования объекта в двоичный образ. При десериализации
объекта этот код используется для извлечения значений атрибутов объекта из
двоичного образа и сохранения в объекте.

Для того, чтобы сделать объект сериализуемым необходимо:
- сделать его класс производным от oess_1::stdsn::serializable_t или от
другого сериализуемого класса;
- использовать в описании класса макрос OESS_SERIALIZER() или
OESS_SERIALIZER_EX();
- описать структуру объекта на языке DDL;
- сгенерировать по DDL описанию вспомогательный код с помощью утилиты
oess_cpp_serializer;
- подключить в проект сгенерированный сериализуемый код.

Может показаться, что для объявления какого-либо типа требуется слишком
много действий. На самом деле это не так. Можно взять в качестве примера
сериализуемыe типы oess_1::db::storage::impl::stream_storage_dir_item_t,
oess_1::db::storage::impl::stream_storage_dir_t:

\code
#include <map>

// Этот заголовочный файл нужен для доступа к типам oess_1::char_t,...
#include <oess_1/defs/h/types.hpp>

// Здесь описывается serializable_t.
#include <oess_1/stdsn/h/serializable.hpp>
// А здесь -- макросы OESS_SERIALIZER(), ...
#include <oess_1/stdsn/h/macro.hpp>

...

class	stream_storage_dir_item_t
	:	public oess_1::stdsn::serializable_t
	{
		typedef oess_1::stdsn::serializable_t base_type_t;
		OESS_SERIALIZER( stream_storage_dir_item_t )

	public :
		stream_storage_dir_item_t();
		stream_storage_dir_item_t(
			const oess_1::uint_t & id,
			const chain_id_t & description_chain );
		stream_storage_dir_item_t(
			const stream_storage_dir_item_t & o );
		virtual ~stream_storage_dir_item_t();

		stream_storage_dir_item_t &
		operator=(
			const stream_storage_dir_item_t & o );

		oess_1::uint_t
		id() const;

		const chain_id_t &
		description_chain() const;

	private :
		oess_1::uint_t	m_id;
		chain_id_t m_description_chain;
	};

...

class	stream_storage_dir_t
	:	public oess_1::stdsn::serializable_t
	{
		typedef oess_1::stdsn::serializable_t base_type_t;
		OESS_SERIALIZER( stream_storage_dir_t )

	public :
		stream_storage_dir_t();
		stream_storage_dir_t(
			const stream_storage_dir_t & o );
		virtual ~stream_storage_dir_t();

		stream_storage_dir_t &
		operator=(
			const stream_storage_dir_t & o );

		oess_1::uint_t
		allocate_id();

		void
		add(
			const std::string & name,
			const oess_1::uint_t & id,
			const chain_id_t & description_chain );

		void
		destroy(
			const std::string & name );

		oess_1::uint_t
		find_id(
			const std::string & name ) const;

		bool
		is_exists(
			const std::string & name ) const;

		std::set< std::string >
		names() const;

		const stream_storage_dir_item_t &
		item(
			const std::string & name ) const;

	private :
		typedef std::map< std::string,
						stream_storage_dir_item_t >
				dir_t;

		dir_t	m_dir;

		oess_1::uint_t	m_id;

		dir_t::iterator
		locate_existent_item(
			const std::string & name );
		dir_t::const_iterator
		locate_existent_item(
			const std::string & name ) const;
	};
...
\endcode

Видно, что в этих двух сериализуемых типах описание сериализуемости
занимает всего по две строки: указание в качестве базового типа класса
oess_1::stdsn::serializable_t и использование OESS_SERIALIZER().
DDL описание для этих типов так же весьма компактное:

\verbatim
{type	oess_1::db::storage::impl::stream_storage_dir_item_t
	{attr m_id	{of	oess_1::uint_t}}
	{attr m_description_chain	{of	oess_1::uint_t}}
}

{type	oess_1::db::storage::impl::stream_storage_dir_t
	{attr m_dir
		{stl-map {key std::string}}
		{of	oess_1::db::storage::impl::stream_storage_dir_item_t}
	}
}
\endverbatim

Сгенерированный вспомогательный код подключается в файл
stream_storage_dir.cpp с помощью нескольких строк:

\code
...
// Этот заголовочный файл содержит описание типов, необходимых для
// работы вспомогательного кода.
#include <oess_1/stdsn/h/inout_templ.hpp>
...
#include "stream_storage_dir.ddl.cpp"
...
\endcode

Остается вопрос запуска утилиты oess_cpp_serializer при компиляции проекта
для генерации stream_storage_dir.ddl.cpp из stream_storage_dir.ddl. Но это
уже вопрос удобства применяемых средств управления компиляцией. Например,
при использовании mxx_ru (http://eao197.narod.ru/mxx_ru) это делается с помощью
нескольких строк в проектном файле:

\verbatim
require	'mxx_ru/cpp'

...
require 'oess_1/util_cpp_serializer/gen'

Mxx_ru::setup_target(
	Mxx_ru::Cpp::Dll_target.new( "oess_1/db/prj.rb" ) {
		...
		ddl_cpp_generator = generator(
			Oess_1::Util_cpp_serializer::Gen.new( self ) )
		...
		sources_root( "storage" ) { 
			...
			sources_root( "impl" ) { 
				...
				cpp_source( "stream_storage_dir.cpp" )
				ddl_cpp_generator.ddl_file( "stream_storage_dir.ddl" )
				...
			}
		}
		...
	}
)
\endverbatim

Для того, чтобы сериализовать объект в какой-либо выходной поток
(см. oess_1::io) необходим дополнительный объект, который будет управлять
формированием образа объекта при сериализации. Класс oent_t является
вершиной иерархии классов, которые занимаются разметкой образа объекта
при сериализации. Класс ient_t является вершиной иерархии классов, выполняющих
обратную задачу -- десериализующих объект из его образа.

Изначально идея состояла в том, что вспомогательный код используется для
предоставления доступа к атрибутам объекта. Но сам объект может быть
сериализован различными способами. Например, только двоичных сериализаций
может быть несколько видов: на основе порядка следования атрибутов
(a-la ASN1 PER) или принципа Tag-Length-Value (a-la ASN1 BER). А еще,
теоритически, возможна сериализация в текствое представление, так же разных
форматов: XML, YAML, Curl-подобный и др. Поэтому сгенерированный для
сериализуемого класса вспомогательный код не знает, как будет выглядеть
полученный после сериализации образ объекта. Эта функциональность вынесена
в семейство классов oent_t/ient_t.

На данный момент поддерживается только один способ упаковки образа объектов,
называемый стандартным (штатным). Он реализуется классами oent_std_t и
ient_std_t. В этом способе все атрибуты сохраняются в двоичном виде, в
той последовательности, в которой они описанны в DDL (похожий подход, но
в более развитой форме используется в ASN1 PER). Унаследованные атрибуты
предшествуют собственным атрибутам объекта. Никаких маркеров, которые могли
бы показать где заканчивается значение одного объекта и начинается значение
второго объекта нет (поэтому этот способ раньше назывался "способом без
маркеров"). Т.к. никаких маркеров в двоичном образе объекта нет, то на
программисте лежит ответственность за то, чтобы из двоичного образа объекта
десериализация осуществлялась в объект нужного типа. Т.е., если в образе
находится объект типа A, а десериализовать образ пытаются в объект типа B,
то ObjESSty никак не сможет этому воспрепятствовать. И результат такой
операции не определен.

Итак, чтобы сериализовать объект необходимо:
- иметь выходной поток, в который будет осуществляться сериализация;
- создать объект типа oent_std_t и связать его с выходным потоком;
- применить к сериализуемому объекту оператор сдвига:
\code
// Сериализуем объект некоторого типа A в std::string для того,
// чтобы этот std::string можно было куда-нибудь передать или
// где-нибудь сохранить.
std::string
serialize( A & a )
	{
		std::string image;
		oess_1::io::obstring_t stream( image );
		oess_1::stdsn::oent_std_t oent( stream );
		oent << a;

		return image;
	}
\endcode

Для того, чтобы десериализовать объект необходимо:
- точно знать, объект какого типа сериализован;
- иметь объект, в который будет происходить десериализация. Тип этого объекта
должен совпадать с типом сериализованного объекта;
- иметь входной поток с сериализованным образом объекта;
- создать объект типа ient_std_t и связать его со входным потоком;
- применить к десериализуемому объекту оператор сдвига:
\code
// Десериализуем объект некоторого типа A из std::string.
A
deserialize( const std::string & image )
	{
		A a;
		oess_1::io::ibstring_t stream( image );
		oess_1::stdsn::ient_std_t ient( stream );
		ient >> a;

		return a;
	}
\endcode

\note Это только общее описание возможностей стандартной сериализации ObjESSty.
	Здесь не затронуты такие важные темы, как значения атрибутов по
	умолчанию (см.\ref oess_1_2_0__optional_fields), расширяемость типов
	(см.\ref oess_1_2_0__extensible_types), механизм subclassing by extension 
	(см.\ref oess_1_2_0__subclassing_by_extension), возможность сохранения
	и повторной сериализации неизвестных расширений
	(см.\ref oess_1_2_0__unknown_extension). Но именно эти возможности делают
	стандартный способ сериализации ObjESSty мощным и уникальным инструментом
	для сериализации сложных C++ типов.

\note Так же здесь не затрагивались вопросы языка DDL, это сделано в
	описании oess_1::scheme.

*/
namespace stdsn
{

	/*!
		\brief Коды ошибок компоненты %stdsn.
	*/
	namespace err {}

}
}
