/*

smart_ref_3: Smart Reference library
Yauheni A. Akhotnikau (C) 2001-2003
eao197@yahoo.com
-------------------------------------------------

Permission is granted to anyone to use this software for any purpose on any
computer system, and to redistribute it freely, subject to the following
restrictions:

1. This software is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

2. The origin of this software must not be misrepresented, either by
   explicit claim or by omission.

3. Altered versions must be plainly marked as such, and must not be
   misrepresented as being the original software.

-------------------------------------------------

*/

/*!
	\file
	\brief Основные классы и макросы.
*/

#if !defined( _SMART_REF_3__SMART_REF_HPP_ )
#define _SMART_REF_3__SMART_REF_HPP_

#include <smart_ref_3/h/declspec.hpp>

namespace smart_ref_3 {

//
// smart_ref_count_t
//

//! Тип счетчика ссылок.
typedef unsigned long ref_count_t;


//
// ref_data_t
//

//! Класс для хранения информации об указателе на объект и
//! о текущем количестве ссылок на этот объект.
class	SMART_REF_3_TYPE	ref_data_t {
	public :
		//! Тип указателя на функцию, уничтожающую хранимый объект.
		typedef void (*pfn_destroyer_t)( void * );

	protected :
		//! Указатель на хранимый объект.
		void *	m_ptr;
		//! Количество ссылок на объект.
		ref_count_t	m_ref_count;
		//! Функция, которая будет удалять объект.
		pfn_destroyer_t	m_destroyer;

	public :
		//! Основной конструктор.
		/*!
			Устанавливает количество ссылок в 1. */
		ref_data_t(
			//! Хранимый объект.
			void * ptr,
			//! Функция, которая будет удалять объект.
			pfn_destroyer_t destroyer );
		~ref_data_t();

		//! Получить указатель на объект.
		void *
		query_ptr() const {
			return m_ptr;
		}

		//! Получить текущее количество ссылок на объект.
		ref_count_t
		query_ref_count() const {
			return m_ref_count;
		}

		//! Уменьшить количество ссылок на 1.
		/*!
			\return \a ref_data если количество ссылок осталось
				большим нуля. В противном случае возвращается 0. */
		static ref_data_t *
		dec_ref_count( ref_data_t * ref_data );

		//! Увеличить количество ссылок на 1.
		/*!
			\return \a ref_data. */
		static ref_data_t *
		inc_ref_count( ref_data_t * ref_data );
};

//
// ref_t
//

//! Базовый тип для всех умных ссылок.
/*!
	\attention Не является \b thread-safe.
*/
class	SMART_REF_3_TYPE	ref_t {
	public :
		//! Основной открытый конструктор.
		ref_t(
			const ref_t & o );
		~ref_t();

		//! Текущее количество ссылок на объект.
		ref_count_t
		query_ref_count() const;

		//! Отказаться от хранения объекта.
		/*!
			Данная ссылка перестает ссылаться на объект.
			Счетчик ссылок уменьшается на 1.

			Объект удаляется только когда счетчик ссылок
			достигнет 0.
		*/
		void
		destroy();

	protected :
		//! Информация о хранимом объекте.
		ref_data_t *	m_ref_data;

		//! Конструктор по-умолчанию доступен только
		//! для производных классов.
		ref_t();

		//! Установить значение атрибута m_ref_data без
		//! изменения количества ссылок.
		void
		set_ref_data(
			ref_data_t * ref_data );

	private :
		//! Защита от копирования.
		ref_t &
		operator=( const ref_t & o );
};

} /* namespace smart_ref_3 */

// Declaration of reference to concrete type

#define SMART_REF_3_DECL_REF_FULL(type, ref_type, ref_type_decl_start)\
ref_type_decl_start ref_type: public smart_ref_3::ref_t {\
	public :\
		ref_type( type * ptr = 0 );\
		ref_type( const ref_type & o );\
		~ref_type();\
\
		ref_type &\
		operator=( const ref_type & o );\
\
		type *\
		query();\
\
		const type *\
		query() const;\
\
		type &\
		operator*();\
		const type &\
		operator*() const;\
\
		type *\
		operator->();\
		const type *\
		operator->() const;\
	protected :\
		static void\
		destroyer( type * obj );\
};

#define SMART_REF_3_DECL_DERIVED_REF_FULL(\
	type, base_ref_type, ref_type, ref_type_decl_start )\
ref_type_decl_start ref_type: public base_ref_type {\
	public :\
		ref_type( type * ptr = 0 );\
		ref_type( const ref_type & o );\
		~ref_type();\
\
		ref_type &\
		operator=( const ref_type & o );\
\
		type *\
		query();\
\
		const type *\
		query() const;\
\
		type &\
		operator*();\
		const type &\
		operator*() const;\
\
		type *\
		operator->();\
		const type *\
		operator->() const;\
};

// Implementation of reference to concrete type

#define SMART_REF_3_IMPL_REF_FULL(type, ref_type)\
ref_type::ref_type( type * ptr ) {\
	if( ptr )\
		set_ref_data( new smart_ref_3::ref_data_t( ptr,\
			(smart_ref_3::ref_data_t::pfn_destroyer_t) destroyer ) );\
}\
ref_type::ref_type( const ref_type & o ) : smart_ref_3::ref_t( o ) {\
}\
ref_type::~ref_type() {\
}\
ref_type &\
ref_type::operator=( const ref_type & o ) {\
	if( this != &o ) {\
		destroy();\
		m_ref_data = smart_ref_3::ref_data_t::inc_ref_count( o.m_ref_data );\
	}\
	return *this;\
}\
type *\
ref_type::query() {\
	if( m_ref_data )\
		return (type *)m_ref_data->query_ptr();\
	return 0;\
}\
const type *\
ref_type::query() const {\
	if( m_ref_data )\
		return (const type *)m_ref_data->query_ptr();\
	return 0;\
}\
type &\
ref_type::operator*() {\
	return *( query() );\
}\
const type &\
ref_type::operator*() const {\
	return *( query() );\
}\
type *\
ref_type::operator->() {\
	return query();\
}\
const type *\
ref_type::operator->() const {\
	return query();\
}\
void \
ref_type::destroyer( type * obj ) {\
	delete obj;\
}

#define SMART_REF_3_IMPL_DERIVED_REF_FULL(type, base_ref_type, ref_type)\
ref_type::ref_type( type * ptr ) : \
	base_ref_type( ptr ) { \
} \
ref_type::ref_type( const ref_type & o ) : base_ref_type( o ) {\
}\
ref_type::~ref_type() {\
}\
ref_type &\
ref_type::operator=( const ref_type & o ) {\
	if( this != &o ) {\
		destroy();\
		m_ref_data = smart_ref_3::ref_data_t::inc_ref_count( o.m_ref_data );\
	}\
	return *this;\
}\
type *\
ref_type::query() {\
	if( m_ref_data )\
		return (type *)m_ref_data->query_ptr();\
	return 0;\
}\
const type *\
ref_type::query() const {\
	if( m_ref_data )\
		return (const type *)m_ref_data->query_ptr();\
	return 0;\
}\
type &\
ref_type::operator*() {\
	return *( query() );\
}\
const type &\
ref_type::operator*() const {\
	return *( query() );\
}\
type *\
ref_type::operator->() {\
	return query();\
}\
const type *\
ref_type::operator->() const {\
	return query();\
}

// Easy declaration and implementation of references

/*!
	\brief Декларация класса умной ссылки.

	Для типа с именем A строит следующее описание:
\code
class ref_A: public smart_ref_3::ref_t {
	public :
		ref_A( A * ptr = 0 );
		ref_A( const ref_A & o );
		~ref_A();

		ref_A &
		operator=( const ref_A & o );

		A *
		query();

		const A *
		query() const;

		A &
		operator*();
		const A &
		operator*() const;

		A *
		operator->();
		const A *
		operator->() const;
	protected :
		static void
		destroyer( A * obj );
};
\endcode

	\param	type	имя типа, для которого строится умная ссылка.
	\param	ref_type_decl_start	квалификатор типа (class, struct)
		который нужно использовать для построения описания класса.
		Так же можен содержать атрибуты экспорта/импорта:
\code
SMART_REF_3_DECL_REF( some_type_t, class __declspec(dllexport) )
\endcode
*/
#define SMART_REF_3_DECL_REF(type, ref_type_decl_start) \
	SMART_REF_3_DECL_REF_FULL(type, ref_##type, ref_type_decl_start)

/*!
	\brief Реализация типа, описанного с помощью макроса
		%SMART_REF_3_DECL_REF().
*/
#define SMART_REF_3_IMPL_REF(type) \
	SMART_REF_3_IMPL_REF_FULL(type, ref_##type)

/*!
	\brief Декларация класса умной ссылки, производного от
		другого класса умной ссылки.

	Предназначен для случаев, когда есть иерархия типов для которой
	нужно построить аналогичную иерархию ссылочных типов. Тогда
	производные ссылки можно передавать в функции, ожидающие
	ссылки на базовые типы. Например:
\code
class	A { ... };
SMART_REF_3_DECL_REF( A, class )
SMART_REF_3_IMPL_REF( A )

void
f( const ref_A & a ) { ... }

class	B : public A { ... };
SMART_REF_3_DECL_DERIVED_REF( B, A, class )
SMART_REF_3_IMPL_DERIVED_REF( B, A )

void
g()
{
	ref_B b( new B() );
	f( b );
}
\endcode

	Для типа с именем B и базовым типом A строит следующее описание:
\code
class ref_B: public ref_A {
	public :
		ref_B( B * ptr = 0 );
		ref_B( const ref_B & o );
		~ref_B();

		ref_B &
		operator=( const ref_B & o );

		B *
		query();

		const B *
		query() const;

		B &
		operator*();
		const B &
		operator*() const;

		B *
		operator->();
		const B *
		operator->() const;
};
\endcode

	\param	type	имя типа, для которого строится умная ссылка.
	\param	base_type	имя базового типа.
	\param	ref_type_decl_start	квалификатор типа (class, struct)
		который нужно использовать для построения описания класса.
		Так же можен содержать атрибуты экспорта/импорта:
\code
SMART_REF_3_DECL_DERIVED_REF(
	some_type_t,
	some_base_type_t,
	class __declspec(dllexport) )
\endcode

*/
#define SMART_REF_3_DECL_DERIVED_REF(type, base_type, ref_type_decl_start) \
	SMART_REF_3_DECL_DERIVED_REF_FULL( \
	type, ref_##base_type, ref_##type, ref_type_decl_start)

/*!
	\brief Реализация типа, построенного с имспользованием макроса
	%SMART_REF_3_DECL_DERIVED_REF().
*/
#define SMART_REF_3_IMPL_DERIVED_REF(type, base_type) \
	SMART_REF_3_IMPL_DERIVED_REF_FULL(type, ref_##base_type, ref_##type)

#endif
