/*

Yauheni A. Akhotnikau (C) 1997-2003
e-mail: 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( _CPP_UTIL_2__UNARY_METHOD_HPP_ )
#define _CPP_UTIL_2__UNARY_METHOD_HPP_

namespace cpp_util_2
{

namespace	impl
{

//
// return_type_detector_t
//

//! Обертка для определения типа возвращаемого значения.
/*!
	Необходимость в этой обертке возникла из-за того, что
	VC6.0 не позволяет возвращать тип void:
\code
void
f() {}

void
call_f()
{
	return f();
}
\endcode

	Поэтому для методов, возвращающих void нужно создавать
	обертки вызова, которые возврают какой-либо тип, например,
	\c int:
\code
void
f() {}

int
call_f()
{
	f();
	return 0;
}
\endcode

	Шаблон %return_type_detector_t берет на себя задачу определения типа
	возвращаемого значения. Для методов, возвращающих void
	создается специализация %return_type_detector_t< void >, которая
	возвращает тип int.
*/
template< class R >
class	return_type_detector_t
{
	public :
		//! Тип, который будет возвращать функция %call_method().
		typedef R return_type_t;
		//! Тип дополнительного аргумента в для функции
		//! %call_method.
		/*!
			Функции call_method(), получающие последним
			аргументом char * предназначены для вызова
			методов, которые реально что-то возвращают.
		*/
		typedef char dummy_arg_type_t;
};

//
// return_type_detector_t< void >
//

//! Спецализация шаблона %return_type_detector_t для методов,
//! возвращающих void.
template<>
class	return_type_detector_t< void >
{
	public :
		//! Тип, который будет возвращать функция %call_method().
		typedef int	return_type_t;
		//! Тип дополнительного аргумента в для функции
		//! %call_method.
		/*!
			Функции call_method(), получающие последним
			аргументом int * предназначены для вызова
			методов, которые ничего возвращают.
		*/
		typedef int dummy_arg_type_t;
};

//
// call_method
//

//! \name Вспомогательные функции, которые осуществляют вызов метода.
//! \{


//! Вызов неконстантного метода, возвращающего не void.
template< class R, class T, class A, class A2 >
R
call_method( T * p, R (T::*pfn)( A ), A2 a, char * )
{
	return (p->*pfn)( a );
}

//! Вызов неконстантного метода, возвращающего void.
template< class R, class T, class A, class A2 >
int
call_method( T * p, void (T::*pfn)( A ), A2 a, int * )
{
	(p->*pfn)( a );
	return 0;
}

//! Вызов константного метода, возвращающего не void.
template< class R, class T, class A, class A2 >
R
call_method( T * p, R (T::*pfn)( A ) const, A2 a, char * )
{
	return (p->*pfn)( a );
}

//! Вызов константного метода, возвращающего void.
template< class R, class T, class A, class A2 >
int
call_method( T * p, void (T::*pfn)( A ) const, A2 a, int * )
{
	(p->*pfn)( a );
	return 0;
}

//! \}

} /* namespace impl */

//
// unary_method_t
//

//! Класс-обертка для вызова унарного неконстантного метода объекта.
/*!
	Подробнее см. \ref cpp_util_2__unary_method.
*/
template< class R, class T, class A >
class	unary_method_t
{
	public :
		//! Тип возвращаемого значения.
		typedef
			typename impl::return_type_detector_t< R >::return_type_t
			return_type_t;

		typedef
			typename impl::return_type_detector_t< R >::dummy_arg_type_t
			dummy_arg_type_t;

		//! Инициализирующий конструктор.
		unary_method_t(
			//! Объект, у которого нужно вызвать метод.
			T * p,
			//! Указатель на вызываемый метод.
			R (T::*pfn)( A ) )
		:
			m_p( p )
		,	m_pfn( pfn )
		{}

		//! Осуществить вызов метода.
		return_type_t
		operator()( A a )
		{
			dummy_arg_type_t * dummy = 0;
			return impl::call_method
				< return_type_t, T, A, A >
				( m_p, m_pfn, a, dummy );
		}

	private :
		//! Объект, у которого нужно вызвать метод.
		T *	m_p;
		//! Указатель на вызываемый метод.
		R (T::*m_pfn)( A );
};

//
// unary_method
//

//! Упрощенный способ создания объекта %unary_method_t.
/*!
	Если есть класс:
\code
class	actor_t
{
	public :
		void
		f( const std::string & a );
};
\endcode

	То создать объект \c unary_method_t для вызова метода f у
	объекта типа actor_t можно двумя способами:
	- сложный способ:
\code
void	call_f_foreach
	(	actor_t & a,
		const std::list< std::string > & l )
{
	std::for_each
		(	l.begin(),
			l.end(),
			cpp_util_2::unary_method_t
				< void, actor_t, const std::string & >
				(	&a, &actor_t::f ) );
}
\endcode
	- простой способ:
\code
void	call_f_foreach
	(	actor_t & a,
		const std::list< std::string > & l )
{
	std::for_each
		(	l.begin(),
			l.end(),
			cpp_util_2::unary_method( &a, &actor_t::f ) );
}
\endcode
*/
template< class R, class T, class A >
unary_method_t< R, T, A >
unary_method( T * p, R (T::*pfn)( A ) )
{
	return unary_method_t< R, T, A >( p, pfn );
}

//
// const_unary_method_t
//

//! Класс-обертка для вызова константного унарного метода объекта.
/*!
	Подробнее см. \ref cpp_util_2__unary_method.
*/
template< class R, class T, class A >
class	const_unary_method_t
{
	public :
		//! Тип возвращаемого значения.
		typedef
			typename impl::return_type_detector_t< R >::return_type_t
			return_type_t;

		typedef
			typename impl::return_type_detector_t< R >::dummy_arg_type_t
			dummy_arg_type_t;

		//! Инициализирующий конструктор.
		const_unary_method_t(
			//! Объект, у которого нужно вызвать метод.
			T * p,
			//! Указатель на вызываемый метод.
			R (T::*pfn)( A ) const )
		:
			m_p( p )
		,	m_pfn( pfn )
		{}

		//! Осуществить вызов метода.
		return_type_t
		operator()( A a )
		{
			dummy_arg_type_t * dummy = 0;
			return impl::call_method
				< return_type_t, T, A, A >
				( m_p, m_pfn, a, dummy );
		}

	private :
		//! Объект, у которого нужно вызвать метод.
		T *	m_p;
		//! Указатель на вызываемый метод.
		R (T::*m_pfn)( A ) const;
};

//
// const_unary_method
//

//! Упрощенный способ создания объекта %const_unary_method_t.
/*!
	Если есть класс:
\code
class	actor_t
{
	public :
		void
		f( const std::string & a ) const;
};
\endcode

	То создать объект \c unary_method_t для вызова метода f у
	объекта типа actor_t можно двумя способами:
	- сложный способ:
\code
void	call_f_foreach
	(	actor_t & a,
		const std::list< std::string > & l )
{
	std::for_each
		(	l.begin(),
			l.end(),
			cpp_util_2::const_unary_method_t
				< void, actor_t, const std::string & >
				(	&a, &actor_t::f ) );
}
\endcode
	- простой способ:
\code
void	call_f_foreach
	(	actor_t & a,
		const std::list< std::string > & l )
{
	std::for_each
		(	l.begin(),
			l.end(),
			cpp_util_2::unary_method( &a, &actor_t::f ) );
}
\endcode
*/
template< class R, class T, class A >
const_unary_method_t< R, T, A >
unary_method( T * p, R (T::*pfn)( A ) const )
{
	return const_unary_method_t< R, T, A >( p, pfn );
}

} /*cpp_util_2 */

#endif
