/*
 * The UDS Collection
 * Copyright (C) 2000, 2001  Julian v. Bock
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *	(See file COPYING for more information)
 */

#ifndef _uds_functional_hh
#define _uds_functional_hh


namespace uds
{

template < class Ret, class Tp >
class generic_function
{
public:
	typedef Ret result_type;
	typedef Tp argument_type;
};


template < class Tp, class ATp >
class ref_t : public generic_function< Tp, ATp >
{
public:
	typedef Tp& ref;
	
	ref_t( Tp v ) : value( v ) {}
	operator Tp& () const { return value; }
	
	template < class Arg >
	typename ref_t< Tp, ATp >::ref
	operator () ( Arg ) const { return value; }
	
	typename ref_t< Tp, ATp >::ref
	operator () () const { return value; }
	
private:
	mutable Tp value;
};

template < class Tp, class ATp >
class ref_t< const Tp, ATp > : public generic_function< Tp, ATp >
{
public:
	typedef const Tp& ref;
	
	ref_t( const Tp& v ) : value( v ) {}
	operator const Tp& () const { return value; }
	
	template < class Arg >
	typename ref_t< Tp, ATp >::ref
	operator () ( Arg ) const { return value; }
	
	typename ref_t< Tp, ATp >::ref
	operator () () const { return value; }
	
private:
	const Tp& value;
};

template < class Tp, class ATp >
class ref_t< Tp&, ATp > : public generic_function< Tp, ATp >
{
public:
	typedef Tp& ref;
	
	ref_t( Tp& v ) : value( v ) {}
	operator Tp& () const { return value; }
	
	template < class Arg >
	typename ref_t< Tp, ATp >::ref
	operator () ( Arg ) const { return value; }
	
	typename ref_t< Tp, ATp >::ref
	operator () () const { return value; }
	
private:
	Tp& value;
};

template < class Tp, class ATp >
class ref_t< const Tp&, ATp > : public generic_function< Tp, ATp >
{
public:
	typedef const Tp& ref;
	
	ref_t( const Tp& v ) : value( v ) {}
	operator const Tp& () const { return value; }
	
	template < class Arg >
	typename ref_t< Tp, ATp >::ref
	operator () ( Arg ) const { return value; }
	
	typename ref_t< Tp, ATp >::ref
	operator () () const { return value; }
	
private:
	const Tp& value;
};


template < class Tp > inline
ref_t< Tp, Tp >
value( Tp x ) { return ref_t< Tp, Tp >( x ); }

template < class ATp, class Tp > inline
ref_t< Tp, ATp >
t_value( Tp x ) { return ref_t< Tp, ATp >( x ); }

template < class Tp > inline
ref_t< Tp&, Tp& >
reference( Tp& x ) { return ref_t< Tp&, Tp& >( x ); }

template < class ATp, class Tp > inline
ref_t< Tp&, ATp >
t_reference( Tp& x ) { return ref_t< Tp&, ATp >( x ); }


// Functionals

/** creates a set of template functionals
	\param name functional name
	\param ret return type
	\param arg argument type
	\param pre pre-action
	\param after after-action
	\author Julian v. Bock */
#define _uds_CreateFunctionals( name, ret, arg, pre, after )                 \
template < class Tp >                                                        \
class name : public generic_function< ret, arg >                             \
{                                                                            \
public:                                                                      \
	const ret&                                                               \
	operator () ( const arg& v ) const { return pre v after; }               \
	                                                                         \
	ret&                                                                     \
	operator () ( arg& v ) const { return pre v after; }                     \
};                                                                           \
                                                                             \
template < class Tp >                                                        \
class name< const Tp > : public generic_function< ret, arg >                 \
{                                                                            \
public:                                                                      \
	const ret&                                                               \
	operator () ( const arg& v ) const { return pre v after; }               \
};                                                                           \
                                                                             \
template < class Tp >                                                        \
class name< Tp& > : public generic_function< ret, arg >                      \
{                                                                            \
public:                                                                      \
	const ret&                                                               \
	operator () ( const arg& v ) const { return pre v after; }               \
	                                                                         \
	ret&                                                                     \
	operator () ( arg& v ) const { return pre v after; }                     \
};                                                                           \
                                                                             \
template < class Tp >                                                        \
class name< const Tp& > : public generic_function< ret, arg >                \
{                                                                            \
public:                                                                      \
	const ret&                                                               \
	operator () ( const arg& v ) const { return pre v after; }               \
};

_uds_CreateFunctionals( identity, Tp, Tp, , )
_uds_CreateFunctionals( dereference, Tp, Tp*, *, )
_uds_CreateFunctionals( address, Tp*, Tp, &, )

_uds_CreateFunctionals( select1st, typename Tp::first_type, Tp, ,.first )
_uds_CreateFunctionals( select2nd, typename Tp::second_type, Tp, ,.second )


// functions with one argument

template < class Ret, class Arg1 >
class fun1_p : public generic_function< Ret, Arg1 >
{
public:
	typedef Ret ( * function_type )( Arg1 );
	
	explicit fun1_p( function_type p ) : f( p ) {}
	
	Ret
	operator () ( Arg1 a1 ) const { return f( a1 ); }
	
private:
	function_type f;
};

template < class Ret, class Arg1 >
class fun1_mp : public generic_function< Ret, Arg1* >
{
public:
	typedef Ret ( Arg1::* function_type )();
	
	explicit fun1_mp( function_type p ) : f( p ) {}
	
	Ret
	operator () ( Arg1* a1 ) const { return ( a1->*f )(); }
	
private:
	function_type f;
};

template < class Ret, class Arg1 >
class fun1_cmp : public generic_function< Ret, const Arg1* >
{
public:
	typedef Ret ( Arg1::* function_type )() const;
	
	explicit fun1_cmp( function_type p ) : f( p ) {}
	
	Ret
	operator () ( const Arg1* a1 ) const { return ( a1->*f )(); }
	
private:
	function_type f;
};

template < class Ret, class Arg1 >
class fun1_mpr : public generic_function< Ret, Arg1& >
{
public:
	typedef Ret ( Arg1::* function_type )();
	
	explicit fun1_mpr( function_type p ) : f( p ) {}
	
	Ret
	operator () ( Arg1& a1 ) const { return ( a1.*f )(); }
	
private:
	function_type f;
};

template < class Ret, class Arg1 >
class fun1_cmpr : public generic_function< Ret, const Arg1& >
{
public:
	typedef Ret ( Arg1::* function_type )() const;
	
	explicit fun1_cmpr( function_type p ) : f( p ) {}
	
	Ret
	operator () ( const Arg1& a1 ) const { return ( a1.*f )(); }
	
private:
	function_type f;
};


template < class Op, class Arg1Op >
class fun1_t : public generic_function< typename Op::result_type,
	typename Arg1Op::argument_type >
{
public:
	fun1_t( const typename Op::function_type& f, const Arg1Op& x1 )
		: op( f ), a1( x1 ) {}
	
	typename Op::result_type
	operator () ( typename Arg1Op::argument_type x ) const
		{ return op( a1( x ) ); }
	
	typename Op::result_type
	operator () () const { return op( a1() ); }
	
private:
	Op		op;
	Arg1Op	a1;
};

template < class Op, class Arg1Op >
inline fun1_t< Op, Arg1Op >
function( Op x, const Arg1Op& a1 )
	{ return fun1_t< Op, Arg1Op >( x, a1 ); }

template < class Ret, class Arg1, class Arg1Op >
inline fun1_t< fun1_p< Ret, Arg1 >, Arg1Op >
function( Ret ( * x )( Arg1 ), const Arg1Op& a1 )
	{ return fun1_t< fun1_p< Ret, Arg1 >, Arg1Op >( x, a1 ); }

template < class Ret, class Arg1, class Arg1Op >
inline fun1_t< fun1_mp< Ret, Arg1 >, Arg1Op >
function( Ret ( Arg1::* x )(), const Arg1Op& a1 )
	{ return fun1_t< fun1_mp< Ret, Arg1 >, Arg1Op >( x, a1 ); }

template < class Ret, class Arg1, class Arg1Op >
inline fun1_t< fun1_cmp< Ret, Arg1 >, Arg1Op >
function( Ret ( Arg1::* x )() const, const Arg1Op& a1 )
	{ return fun1_t< fun1_cmp< Ret, Arg1 >, Arg1Op >( x, a1 ); }

template < class Ret, class Arg1, class Arg1Op >
inline fun1_t< fun1_mpr< Ret, Arg1 >, Arg1Op >
function_ref( Ret ( Arg1::* x )(), const Arg1Op& a1 )
	{ return fun1_t< fun1_mpr< Ret, Arg1 >, Arg1Op >( x, a1 ); }

template < class Ret, class Arg1, class Arg1Op >
inline fun1_t< fun1_cmpr< Ret, Arg1 >, Arg1Op >
function_ref( Ret ( Arg1::* x )() const, const Arg1Op& a1 )
	{ return fun1_t< fun1_cmpr< Ret, Arg1 >, Arg1Op >( x, a1 ); }


// functions with two arguments

template < class Ret, class Arg1, class Arg2 >
class fun2_p : public generic_function< Ret, Arg1 >
{
public:
	typedef Ret ( * function_type )( Arg1, Arg2 );
	
	explicit fun2_p( function_type p ) : f( p ) {}
	
	Ret
	operator () ( Arg1 a1, Arg2 a2 ) const { return f( a1, a2 ); }
	
private:
	function_type f;
};

template < class Ret, class Arg1, class Arg2 >
class fun2_mp : public generic_function< Ret, Arg1* >
{
public:
	typedef Ret ( Arg1::* function_type )( Arg2 );
	
	explicit fun2_mp( function_type p ) : f( p ) {}
	
	Ret
	operator () ( Arg1* a1, Arg2 a2 ) const { return ( a1->*f )( a2 ); }
	
private:
	function_type f;
};

template < class Ret, class Arg1, class Arg2 >
class fun2_cmp : public generic_function< Ret, const Arg1* >
{
public:
	typedef Ret ( Arg1::* function_type )( Arg2 ) const;
	
	explicit fun2_cmp( function_type p ) : f( p ) {}
	
	Ret
	operator () ( const Arg1* a1, Arg2 a2 ) const { return ( a1->*f )( a2 ); }
	
private:
	function_type f;
};

template < class Ret, class Arg1, class Arg2 >
class fun2_mpr : public generic_function< Ret, Arg1& >
{
public:
	typedef Ret ( Arg1::* function_type )( Arg2 );
	
	explicit fun2_mpr( function_type p ) : f( p ) {}
	
	Ret
	operator () ( Arg1& a1, Arg2 a2 ) const { return ( a1.*f )( a2 ); }
	
private:
	function_type f;
};

template < class Ret, class Arg1, class Arg2 >
class fun2_cmpr : public generic_function< Ret, const Arg1& >
{
public:
	typedef Ret ( Arg1::* function_type )( Arg2 ) const;
	
	explicit fun2_cmpr( function_type p ) : f( p ) {}
	
	Ret
	operator () ( const Arg1& a1, Arg2 a2 ) const { return ( a1.*f )( a2 ); }
	
private:
	function_type f;
};


template < class Op, class Arg1Op, class Arg2Op >
class fun2_t : public generic_function< typename Op::result_type,
	typename Arg1Op::argument_type >
{
public:
	fun2_t( const typename Op::function_type& f, const Arg1Op& x1,
		const Arg2Op& x2 )
		: op( f ), a1( x1 ), a2( x2 ) {}
	
	typename Op::result_type
	operator () ( typename Arg1Op::argument_type x ) const
		{ return op( a1( x ), a2( x ) ); }
	
	typename Op::result_type
	operator () () const { return op( a1(), a2() ); }
	
private:
	Op		op;
	Arg1Op	a1;
	Arg2Op	a2;
};

template < class Op, class Arg1Op, class Arg2Op >
inline fun2_t< Op, Arg1Op, Arg2Op >
function( Op x, const Arg1Op& a1, const Arg2Op& a2 )
	{ return fun2_t< Op, Arg1Op, Arg2Op >( x, a1, a2 ); }

template < class Ret, class Arg1, class Arg1Op, class Arg2, class Arg2Op >
inline fun2_t< fun2_p< Ret, Arg1, Arg2 >, Arg1Op, Arg2Op >
function( Ret ( * x )( Arg1, Arg2 ), const Arg1Op& a1, const Arg2Op& a2 )
	{ return fun2_t< fun2_p< Ret, Arg1, Arg2 >, Arg1Op, Arg2Op >
	( x, a1, a2 ); }

template < class Ret, class Arg1, class Arg1Op, class Arg2, class Arg2Op >
inline fun2_t< fun2_mp< Ret, Arg1, Arg2 >, Arg1Op, Arg2Op >
function( Ret ( Arg1::* x )( Arg2 ), const Arg1Op& a1, const Arg2Op& a2 )
	{ return fun2_t< fun2_mp< Ret, Arg1, Arg2 >, Arg1Op, Arg2Op >
	( x, a1, a2 ); }

template < class Ret, class Arg1, class Arg1Op, class Arg2, class Arg2Op >
inline fun2_t< fun2_cmp< Ret, Arg1, Arg2 >, Arg1Op, Arg2Op >
function( Ret ( Arg1::* x )( Arg2 ) const, const Arg1Op& a1, const Arg2Op& a2 )
	{ return fun2_t< fun2_cmp< Ret, Arg1, Arg2 >, Arg1Op, Arg2Op >
	( x, a1, a2 ); }

template < class Ret, class Arg1, class Arg1Op, class Arg2, class Arg2Op >
inline fun2_t< fun2_mpr< Ret, Arg1, Arg2 >, Arg1Op, Arg2Op >
function_ref( Ret ( Arg1::* x )( Arg2 ), const Arg1Op& a1, const Arg2Op& a2 )
	{ return fun2_t< fun2_mpr< Ret, Arg1, Arg2 >, Arg1Op, Arg2Op >
	( x, a1, a2 ); }

template < class Ret, class Arg1, class Arg1Op, class Arg2, class Arg2Op >
inline fun2_t< fun2_cmpr< Ret, Arg1, Arg2 >, Arg1Op, Arg2Op >
function_ref( Ret ( Arg1::* x )( Arg2 ) const, const Arg1Op& a1, const Arg2Op& a2 )
	{ return fun2_t< fun2_cmpr< Ret, Arg1, Arg2 >, Arg1Op, Arg2Op >
	( x, a1, a2 ); }


// functions with three arguments

template < class Ret, class Arg1, class Arg2, class Arg3 >
class fun3_p : public generic_function< Ret, Arg1 >
{
public:
	typedef Ret ( * function_type )( Arg1, Arg2, Arg3 );
	
	explicit fun3_p( function_type p ) : f( p ) {}
	
	Ret
	operator () ( Arg1 a1, Arg2 a2, Arg3 a3 ) const { return f( a1, a2, a3 ); }
	
private:
	function_type f;
};

template < class Ret, class Arg1, class Arg2, class Arg3 >
class fun3_mp : public generic_function< Ret, Arg1* >
{
public:
	typedef Ret ( Arg1::* function_type )( Arg2, Arg3 );
	
	explicit fun3_mp( function_type p ) : f( p ) {}
	
	Ret
	operator () ( Arg1* a1, Arg2 a2, Arg3 a3 ) const { return ( a1->*f )( a2, a3 ); }
	
private:
	function_type f;
};

template < class Ret, class Arg1, class Arg2, class Arg3 >
class fun3_cmp : public generic_function< Ret, const Arg1* >
{
public:
	typedef Ret ( Arg1::* function_type )( Arg2, Arg3 ) const;
	
	explicit fun3_cmp( function_type p ) : f( p ) {}
	
	Ret
	operator () ( const Arg1* a1, Arg2 a2, Arg3 a3 ) const { return ( a1->*f )( a2, a3 ); }
	
private:
	function_type f;
};

template < class Ret, class Arg1, class Arg2, class Arg3 >
class fun3_mpr : public generic_function< Ret, Arg1& >
{
public:
	typedef Ret ( Arg1::* function_type )( Arg2, Arg3 );
	
	explicit fun3_mpr( function_type p ) : f( p ) {}
	
	Ret
	operator () ( Arg1& a1, Arg2 a2, Arg3 a3 ) const { return ( a1.*f )( a2, a3 ); }
	
private:
	function_type f;
};

template < class Ret, class Arg1, class Arg2, class Arg3 >
class fun3_cmpr : public generic_function< Ret, const Arg1& >
{
public:
	typedef Ret ( Arg1::* function_type )( Arg2, Arg3 ) const;
	
	explicit fun3_cmpr( function_type p ) : f( p ) {}
	
	Ret
	operator () ( const Arg1& a1, Arg2 a2, Arg3 a3 ) const { return ( a1.*f )( a2, a3 ); }
	
private:
	function_type f;
};


template < class Op, class Arg1Op, class Arg2Op, class Arg3Op, class Arg >
class fun3_t : public generic_function< typename Op::result_type,
	Arg >
{
public:
	fun3_t( const typename Op::function_type& f, const Arg1Op& x1,
		const Arg2Op& x2, const Arg3Op& x3 )
		: op( f ), a1( x1 ), a2( x2 ), a3( x3 ) {}
	
	typename Op::result_type
	operator () ( Arg x ) const
		{ return op( a1( x ), a2( x ), a3( x ) ); }
	
	typename Op::result_type
	operator () () const { return op( a1(), a2(), a3() ); }
	
private:
	Op		op;
	Arg1Op	a1;
	Arg2Op	a2;
	Arg3Op	a3;
};

template < class Op, class Arg1Op, class Arg2Op, class Arg3Op >
inline fun3_t< Op, Arg1Op, Arg2Op, Arg3Op, typename Arg1Op::argument_type >
function( Op x, const Arg1Op& a1, const Arg2Op& a2 )
	{ return fun3_t< Op, Arg1Op, Arg2Op, Arg3Op, typename Arg1Op::argument_type >( x, a1, a2, a3 ); }

template < class Ret, class Arg1, class Arg1Op, class Arg2, class Arg2Op, class Arg3, class Arg3Op >
inline fun3_t< fun3_p< Ret, Arg1, Arg2, Arg3 >, Arg1Op, Arg2Op, Arg3Op, typename Arg1Op::argument_type >
function( Ret ( * x )( Arg1, Arg2, Arg3 ), const Arg1Op& a1, const Arg2Op& a2, const Arg3Op& a3 )
	{ return fun3_t< fun3_p< Ret, Arg1, Arg2, Arg3 >, Arg1Op, Arg2Op, Arg3Op, typename Arg1Op::argument_type >
	( x, a1, a2, a3 ); }

template < class Ret, class Arg1, class Arg1Op, class Arg2, class Arg2Op, class Arg3, class Arg3Op >
inline fun3_t< fun3_mp< Ret, Arg1, Arg2, Arg3 >, Arg1Op, Arg2Op, Arg3Op, typename Arg1Op::argument_type >
function( Ret ( Arg1::* x )( Arg2, Arg3 ), const Arg1Op& a1, const Arg2Op& a2, const Arg3Op& a3 )
	{ return fun3_t< fun3_mp< Ret, Arg1, Arg2, Arg3 >, Arg1Op, Arg2Op, Arg3Op, typename Arg1Op::argument_type >
	( x, a1, a2, a3 ); }

template < class Ret, class Arg1, class Arg1Op, class Arg2, class Arg2Op, class Arg3, class Arg3Op >
inline fun3_t< fun3_cmp< Ret, Arg1, Arg2, Arg3 >, Arg1Op, Arg2Op, Arg3Op, typename Arg1Op::argument_type >
function( Ret ( Arg1::* x )( Arg2, Arg3 ) const, const Arg1Op& a1, const Arg2Op& a2, const Arg3Op& a3 )
	{ return fun3_t< fun3_cmp< Ret, Arg1, Arg2, Arg3 >, Arg1Op, Arg2Op, Arg3Op, typename Arg1Op::argument_type >
	( x, a1, a2, a3 ); }

template < class Ret, class Arg1, class Arg1Op, class Arg2, class Arg2Op, class Arg3, class Arg3Op >
inline fun3_t< fun3_mpr< Ret, Arg1, Arg2, Arg3 >, Arg1Op, Arg2Op, Arg3Op, typename Arg1Op::argument_type >
function_ref( Ret ( Arg1::* x )( Arg2, Arg3 ), const Arg1Op& a1, const Arg2Op& a2, const Arg3Op& a3 )
	{ return fun3_t< fun3_mpr< Ret, Arg1, Arg2, Arg3 >, Arg1Op, Arg2Op, Arg3Op, typename Arg1Op::argument_type >
	( x, a1, a2, a3 ); }

template < class Ret, class Arg1, class Arg1Op, class Arg2, class Arg2Op, class Arg3, class Arg3Op >
inline fun3_t< fun3_cmpr< Ret, Arg1, Arg2, Arg3 >, Arg1Op, Arg2Op, Arg3Op, typename Arg1Op::argument_type >
function_ref( Ret ( Arg1::* x )( Arg2, Arg3 ) const, const Arg1Op& a1, const Arg2Op& a2, const Arg3Op& a3 )
	{ return fun3_t< fun3_cmpr< Ret, Arg1, Arg2, Arg3 >, Arg1Op, Arg2Op, Arg3Op, typename Arg1Op::argument_type >
	( x, a1, a2, a3 ); }

}

#endif _uds_functional_hh
