/*!

\page stdsn_shptr	Необходимость класса oess_1::stdsn::shptr_t

\note
	Здесь описана только базовая информация о классе oess_1::stdsn::shptr_t.
	Более подробную информацию о реализации умных указателей в ObjESSty
	можно получить здесь: \ref oess_1_4_0__stdsn__shptr_skeleton.

\section stdsn_shptr__reason	Причина появления

Среди типов атрибутов, которые может сериализовать ObjESSty есть
указатель на объект, производный от oess_1::stdsn::serializable_t.
Это делает возможным сериализацию объектов, чей тип заранее
не известен -- известно, только то, что все они производны от
одного корня:

\par Пример 1.
\verbatim
||
|| Объекты-элементы какого-то прикладного протокола.
||
{type	comm_protocol::request_t
}

{type	comm_protocol::reply_t
	{super	comm_protocol::request_t}

	{attr	m_result	{of	oess_1::uint_t}}
}

{type	comm_protocol::handshake_req_t
	{super	comm_protocol::request_t}

	{attr	m_client_name	{of	std::string}}
	{attr	m_connection_type	{of	oess_1::uint_t}}
}

{type	comm_protocol::hanshake_reply_t
	{super	comm_protocol::reply_t}

	{attr	m_session_id	{of	oess_1::uint_t}}
}

||
|| Тип объекта, который передается между клиентом и
|| сервером. Каждая сторона знает, что из коммуникационного
|| канала нужно считать объект comm_protocol::message_t,
|| а затем определять, что именно содержит сообщение.
||
{type	comm_protocol::message_t
	|| Реально, в этом атрибуте может содержаться
	|| как handshake_req_t, так и handshake_reply_t...
	{attr	m_content	{of {ptr}	comm_protocol::request_t}}
}
\endverbatim

\par Пример 2.
\verbatim
||
|| Элементы изображения.
||
{type {abstract}	image::shape_t
}

{type	image::circle_t
	{super	image::shape_t}
}

{type	image::rectangle_t
	{super	image::shape_t}
}

{type	image::curve_t
	{super	image::shape_t}
}

||
|| Само изображение.
||
{type	image::image_t
	{attr	m_name	{of	std::string}}
	{attr	m_items	{stl-list}	{of {ptr}	image::shape_t}}
}
\endverbatim

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

Проблема заключается в том, что при использовании простого
атрибута-указателя ObjESSty никак не контролирует время жизни объекта
по этому указателю. Применительно к классу comm_protocol::message_t
это означает, что программист должен уничтожать объект по указателю
m_content не только в деструкторе, но и перед каждой операцией
десериализации (т.к. после десериализации m_content примет новое
значение, а старое значение будет потеряно).
Так же программист должен очень внимательно обходится с оператором
копирования для сериализуемого типа, содержащего атрибуты-указатели.

Т.е. получается, что применяя простые атрибуты-указатели
сериализуемый тип должен быть осложнен контролем атрибута-указателя:

\code
class	message_t :
	public oess_1::stdsn::serializable_t
{
	OESS_SERIALIZER( message_t )
	public :
		message_t()
			:
				m_content( 0 )
		{
		}
		virtual ~message_t()
		{
			if( m_content )
				delete m_content;
		}

	protected :
		// Перед десериализацией нужно уничтожить
		// старое значение m_content.
		virtual void
		oess_pre_unpack()
		{
			if( m_content )
			{
				// Копия указателя для защиты от
				// исключений.
				request_t * p = m_content;
				m_content = 0;

				delete p;
			}
		}

	private :
		// Конструктор копирования и оператор
		// копирования закрыты, чтобы невозможно
		// было копировать объекты message_t из-за
		// атрибута m_content.
		message_t( const message_t & );
		message_t &
		operator=( const message_t & );

		request_t *	m_content;
};
\endcode

При этом нельзя воспользоваться специально предназначенными
для целей контроля указателей классами (например, std::auto_ptr),
т.к. ObjESSty умеет работать только с атрибутом-указателем.

Во втором примере, со списком указателей, ситуация осложняется
тем, что нужно контролировать множество указателей.

Для преодоления этих проблем и был реализован класс
oess_1::stdsn::shptr_t.

\section stdsn_shptr__class	Класс oess_1::stdsn::shptr_t

\subsection stdsn_shptr__class__demands	Требования

Главной целью при создании класса oess_1::stdsn::shptr_t
являлось предоставление ObjESSty возможности контроля
атрибутов-указателей сериализуемых объектов. В принципе, для этого
можно было бы использовать класс std::auto_ptr. Но с классами,
подобными std::auto_ptr, возникают проблемы при использовании
их в качестве элементов контейнеров.

Дело в том, что класс std::auto_ptr является единственным владельцем
указателя. Оператор копирования std::auto_ptr просто передает
права владения и, при этом, правый операнд присваивания вообще
теряет указатель:

\code
std::auto_ptr< Some_class > old_ptr( new Some_class() );
// Сейчас 0 != old_ptr.get();

std::auto_ptr< Some_class > new_ptr;
// Сейчас 0 != old_ptr.get();
// 0 == new_ptr.get();

// Передаем владение указателем.
new_ptr = old_ptr;

// Сейчас 0 == old_ptr.get();
// 0 != new_ptr.get();
\endcode

Такая семантика оператора присваивания означает, что правый
операнд не может быть константной ссылкой (т.к. он модифицируется
в результате операции). Это делает невозможным создание
контейнеров, элементами которых являются std::auto_ptr:

\code
typedef std::auto_ptr< Some_class > some_auto_ptr_t;
typedef std::list< some_auto_ptr_t > ptr_list_t;

void
f()
{
	ptr_list_t l;
	some_auto_ptr_t p( new Some_class() );

	// НЕ КОМПИЛИРУЕТСЯ!
	// Т.к. нет оператора копирования для
	// const some_auto_ptr_t &
	l.push_back( p );
}
\endcode

Т.о. для ObjESSty требовался такой класс, который бы определял
операции копирования, правыми частями которых являлись бы
константные ссылки:

\code
class	some_ptr_t
{
	public :
		some_ptr_t( const some_ptr_t & );
		some_ptr_t &
		operator=( const some_ptr_t & );
};
\endcode

В таком случае вставал вопрос: "Что делать с объектом, на который
ссылается умный указатель, при копировании указателей?" Можно было
бы попробовать делать копию этого объекта. Но это потребовало бы
введения в oess_1::stdsn::serializable_t виртуального метода
для клонирования. В свою очередь, клонирование для некоторых
сериализуемых объектов могло оказаться черезвычайно дорогой
операцией.

Поэтому класс oess_1::stdsn::shptr_t реализует т.н. "разделямый
указатель". Т.е. существует один объект, указатель на который
хранится в нескольких экземплярах shptr_t. Все экземпляры
shptr_t используют один счетчик ссылок на объект.
Объект уничтожается когда уничтожается последний ссылающийся
на него shptr_t. При копировании в shptr_t-приемник
передаются указатели на контролируемый объект и на счетчик ссылок,
после чего счетчик ссылок увеличивается на 1. При уничтожении
shptr_t (либо в левом операнде операции копирования)
происходит уменьшение количества ссылок. Когда счетчик ссылок
обнуляется -- уничтожается контролируемый объект.

\subsection stdsn_shptr__class__special	Особенности

Традиционно, классы умных указателей делают в виде шаблонов
(C++ template), например, std::auto_ptr или boost::shared_ptr.
Для текущего состояния ObjESSty не представляется возможным
реализовать сериализацию шаблонных классов. Поэтому, класс
shptr_t должен был быть конкретным классом.

При этом вряд ли были бы полезны операторы разыменования
указателей (вроде operator*(), operator->()), т.к. класс
shptr_t оперирует только указателем на абстрактный
класс serializable_t. Программисту все равно пришлось бы
приводить serializable_t* или serializable_t& к какому-то
своему типу.

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

\code
class	request_t : public oess_1::stdsn::serializable_t
{
	OESS_SERIALIZER( request_t )
	...
};
...
class	message_t : public oess_1::stdsn::serializable_t
{
	OESS_SERIALIZER( message_t )
	private :
		shptr_t< request_t >	m_content;
};
\endcode

Проблема в том, что нельзя отобразить в DDL С++ конструкцию
<tt>shptr_t< request_t > m_content</tt>.

Поэтому класс oess_1::stdsn::shptr_t предназначен
быть базовым классом для конкретных умных указателей:

\code
class	request_t : public oess_1::stdsn::serializable_t
{
	OESS_SERIALIZER( request_t )
	...
};
class	request_ptr_t : public oess_1::stdsn::shptr_t
{
	OESS_SERIALIZER( request_t )
	// Реализованные вручную операторы разыменования
	// указателей.
	...
};
class	message_t : public oess_1::stdsn::serializable_t
{
	OESS_SERIALIZER( message_t )
	private :
		request_ptr_t	m_content;
}
\endcode

Что позволяет описать на DDL следующую схему:

\verbatim
{type	request_t
	...
}
{type	request_ptr_t
	{super oess_1::stdsn::shptr_t}
}
...
{type	message_t
	{attr	m_content	{of request_t}}
}
\endverbatim

Естественно, что описывать операторы разыменования в каждом
конкретном классе умного указателя слишком трудоемко. Для
облегчения этой работы предназначен макрос OESS_1_SHPTR_IFACE(),
что позволяет описать класс request_ptr_t всего несколькими
строчками:

\code
class	request_ptr_t : public oess_1::stdsn::shptr_t
{
	OESS_SERIALIZER( request_ptr_t )
	OESS_1_SHPTR_IFACE( request_ptr_t, request_t,
		oess_1::stdsn::shptr_t )
};
\endcode

\subsection stdsn_shptr__class__scheme	oess_1::stdsn::shptr_t и схемы данных

Схемы данных описываются в DDL файлах. DDL файл подается на вход
программе oess_cpp_serializer для генерации вспомогательного кода
сериализации/десериализации. В этом случае DDL файлы описывают схемы
сериализации.

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

Применительно к oess_1::stdsn::shptr_t это
означает, что в DDL файле не нужно делать описание shptr_t. Нужно
в C++ коде директивой \c #include подключить заголовочный файл
oess_1/stdsn/h/shptr.hpp, а линкеру указать библиотеку
oess_stdsn.x.x.x.

*/

