// This file is part of the uSTL library, an STL implementation.
//
// Copyright (c) 2007-2009 by Mike Sharov <msharov@users.sourceforge.net>
//
// This implementation is adapted from the Loki library, distributed under
// the MIT license with Copyright (c) 2001 by Andrei Alexandrescu.

#ifndef TRAITS_H_4AA3DDE15E16C947392711ED08FB1FF6
#define TRAITS_H_4AA3DDE15E16C947392711ED08FB1FF6

#include "typelist.h"

namespace ustl {
namespace tm {
namespace {

//----------------------------------------------------------------------
// Type classes and type modifiers
//----------------------------------------------------------------------

typedef tl::Seq<unsigned char, unsigned short, unsigned, unsigned long>::Type
							StdUnsignedInts;
typedef tl::Seq<signed char, short, int, long>::Type	StdSignedInts;
typedef tl::Seq<bool, char, wchar_t>::Type		StdOtherInts;
typedef tl::Seq<float, double>::Type			StdFloats;

template <typename U> struct AddPointer		{ typedef U* Result; };
template <typename U> struct AddPointer<U&>	{ typedef U* Result; };
template <typename U> struct AddReference	{ typedef U& Result; };
template <typename U> struct AddReference<U&>	{ typedef U& Result; };
template <>           struct AddReference<void>	{ typedef NullType Result; };
template <typename U> struct AddParameterType	{ typedef const U& Result; };
template <typename U> struct AddParameterType<U&> { typedef U& Result; };
template <>           struct AddParameterType<void> { typedef NullType Result; };

//----------------------------------------------------------------------
// Function pointer testers
//----------------------------------------------------------------------
// Macros expand to numerous parameters

template <typename T>
struct IsFunctionPointerRaw { enum { result = false}; };
template <typename T>
struct IsMemberFunctionPointerRaw { enum { result = false}; };

#define TM_FPR_MAXN		9
#define TM_FPR_TYPE(n)		PASTE(T,n)
#define TM_FPR_TYPENAME(n)	typename TM_FPR_TYPE(n)

// First specialize for regular functions
template <typename T>
struct IsFunctionPointerRaw<T(*)(void)> 
{enum {result = true};};

#define TM_FPR_SPEC(n)		\
template <typename T, COMMA_LIST(n, TM_FPR_TYPENAME)>		\
struct IsFunctionPointerRaw<T(*)(COMMA_LIST(n, TM_FPR_TYPE))>	\
{ enum { result = true }; }

LIST (TM_FPR_MAXN, TM_FPR_SPEC, ;);

// Then for those with an ellipsis argument
template <typename T>
struct IsFunctionPointerRaw<T(*)(...)> 
{enum {result = true};};

#define TM_FPR_SPEC_ELLIPSIS(n)	\
template <typename T, COMMA_LIST(n, TM_FPR_TYPENAME)>			\
struct IsFunctionPointerRaw<T(*)(COMMA_LIST(n, TM_FPR_TYPE), ...)>	\
{ enum { result = true }; }

LIST (TM_FPR_MAXN, TM_FPR_SPEC_ELLIPSIS, ;);

// Then for member function pointers
template <typename T, typename S>
struct IsMemberFunctionPointerRaw<T (S::*)(void)> 
{ enum { result = true }; };

#define TM_MFPR_SPEC(n)		\
template <typename T, typename S, COMMA_LIST(n, TM_FPR_TYPENAME)>	\
struct IsMemberFunctionPointerRaw<T (S::*)(COMMA_LIST(n, TM_FPR_TYPE))>	\
{ enum { result = true };};

LIST (TM_FPR_MAXN, TM_MFPR_SPEC, ;);

// Then for member function pointers with an ellipsis argument
template <typename T, typename S>
struct IsMemberFunctionPointerRaw<T (S::*)(...)> 
{ enum { result = true }; };

#define TM_MFPR_SPEC_ELLIPSIS(n)		\
template <typename T, typename S, COMMA_LIST(n, TM_FPR_TYPENAME)>	\
struct IsMemberFunctionPointerRaw<T (S::*)(COMMA_LIST(n, TM_FPR_TYPE), ...)> \
{ enum { result = true }; };

LIST (TM_FPR_MAXN, TM_MFPR_SPEC_ELLIPSIS, ;);

// Then for const member function pointers (getting tired yet?)
template <typename T, typename S>
struct IsMemberFunctionPointerRaw<T (S::*)(void) const> 
{ enum { result = true }; };

#define TM_CMFPR_SPEC(n)	\
template <typename T, typename S, COMMA_LIST(n, TM_FPR_TYPENAME)>	\
struct IsMemberFunctionPointerRaw<T (S::*)(COMMA_LIST(n, TM_FPR_TYPE)) const>	\
{ enum { result = true };};

LIST (TM_FPR_MAXN, TM_CMFPR_SPEC, ;);

// Finally for const member function pointers with an ellipsis argument (whew!)
template <typename T, typename S>
struct IsMemberFunctionPointerRaw<T (S::*)(...) const> 
{ enum { result = true }; };

#define TM_CMFPR_SPEC_ELLIPSIS(n)		\
template <typename T, typename S, COMMA_LIST(n, TM_FPR_TYPENAME)>	\
struct IsMemberFunctionPointerRaw<T (S::*)(COMMA_LIST(n, TM_FPR_TYPE), ...) const> \
{ enum { result = true }; };

LIST (TM_FPR_MAXN, TM_CMFPR_SPEC_ELLIPSIS, ;);

#undef TM_FPR_SPEC
#undef TM_FPR_SPEC_ELLIPSIS
#undef TM_MFPR_SPEC
#undef TM_MFPR_SPEC_ELLIPSIS
#undef TM_CMFPR_SPEC
#undef TM_CMFPR_SPEC_ELLIPSIS
#undef TM_FPR_TYPENAME
#undef TM_FPR_TYPE
#undef TM_FPR_MAXN

} // namespace
    
//----------------------------------------------------------------------
// Type traits template
//----------------------------------------------------------------------

/// Figures out at compile time various properties of any given type
/// Invocations (T is a type, TypeTraits<T>::Propertie):
///
/// - isPointer       : returns true if T is a pointer type
/// - PointeeType     : returns the type to which T points if T is a pointer 
///                     type, NullType otherwise
/// - isReference     : returns true if T is a reference type
/// - ReferredType    : returns the type to which T refers if T is a reference 
///                     type, NullType otherwise
/// - isMemberPointer : returns true if T is a pointer to member type
/// - isStdUnsignedInt: returns true if T is a standard unsigned integral type
/// - isStdSignedInt  : returns true if T is a standard signed integral type
/// - isStdIntegral   : returns true if T is a standard integral type
/// - isStdFloat      : returns true if T is a standard floating-point type
/// - isStdArith      : returns true if T is a standard arithmetic type
/// - isStdFundamental: returns true if T is a standard fundamental type
/// - isUnsignedInt   : returns true if T is a unsigned integral type
/// - isSignedInt     : returns true if T is a signed integral type
/// - isIntegral      : returns true if T is a integral type
/// - isFloat         : returns true if T is a floating-point type
/// - isArith         : returns true if T is a arithmetic type
/// - isFundamental   : returns true if T is a fundamental type
/// - ParameterType   : returns the optimal type to be used as a parameter for 
///                     functions that take Ts
/// - isConst         : returns true if T is a const-qualified type
/// - NonConstType    : Type with removed 'const' qualifier from T, if any
/// - isVolatile      : returns true if T is a volatile-qualified type
/// - NonVolatileType : Type with removed 'volatile' qualifier from T, if any
/// - UnqualifiedType : Type with removed 'const' and 'volatile' qualifiers from 
///                     T, if any
/// - ConstParameterType: returns the optimal type to be used as a parameter 
///                       for functions that take 'const T's
///
template <typename T>
class TypeTraits {
private:
    #define TMTT1	template <typename U> struct
    #define TMTT2	template <typename U, typename V> struct
    TMTT1 ReferenceTraits	{ enum { result = false }; typedef U ReferredType; };
    TMTT1 ReferenceTraits<U&>	{ enum { result = true  }; typedef U ReferredType; };
    TMTT1 PointerTraits		{ enum { result = false }; typedef NullType PointeeType; };
    TMTT1 PointerTraits<U*>	{ enum { result = true  }; typedef U PointeeType; };
    TMTT1 PointerTraits<U*&>	{ enum { result = true  }; typedef U PointeeType; };
    TMTT1 PToMTraits		{ enum { result = false }; };
    TMTT2 PToMTraits<U V::*>	{ enum { result = true  }; };
    TMTT2 PToMTraits<U V::*&>	{ enum { result = true  }; };
    TMTT1 FunctionPointerTraits	{ enum { result = IsFunctionPointerRaw<U>::result }; };
    TMTT1 PToMFunctionTraits	{ enum { result = IsMemberFunctionPointerRaw<U>::result }; };
    TMTT1 UnConst		{ typedef U Result;  enum { isConst = false }; };
    TMTT1 UnConst<const U>	{ typedef U Result;  enum { isConst = true  }; };
    TMTT1 UnConst<const U&>	{ typedef U& Result; enum { isConst = true  }; };
    TMTT1 UnVolatile		{ typedef U Result;  enum { isVolatile = false }; };
    TMTT1 UnVolatile<volatile U>{ typedef U Result;  enum { isVolatile = true  }; };
    TMTT1 UnVolatile<volatile U&> {typedef U& Result;enum { isVolatile = true  }; };
    #undef TMTT2
    #undef TMTT1
public:
    typedef typename UnConst<T>::Result 
	NonConstType;
    typedef typename UnVolatile<T>::Result 
	NonVolatileType;
    typedef typename UnVolatile<typename UnConst<T>::Result>::Result 
	UnqualifiedType;
    typedef typename PointerTraits<UnqualifiedType>::PointeeType 
	PointeeType;
    typedef typename ReferenceTraits<T>::ReferredType 
	ReferredType;

    enum { isConst          = UnConst<T>::isConst };
    enum { isVolatile       = UnVolatile<T>::isVolatile };
    enum { isReference      = ReferenceTraits<UnqualifiedType>::result };
    enum { isFunction       = FunctionPointerTraits<typename AddPointer<T>::Result >::result };
    enum { isFunctionPointer= FunctionPointerTraits<
				    typename ReferenceTraits<UnqualifiedType>::ReferredType >::result };
    enum { isMemberFunctionPointer= PToMFunctionTraits<
				    typename ReferenceTraits<UnqualifiedType>::ReferredType >::result };
    enum { isMemberPointer  = PToMTraits<
				    typename ReferenceTraits<UnqualifiedType>::ReferredType >::result ||
				    isMemberFunctionPointer };
    enum { isPointer        = PointerTraits<
				    typename ReferenceTraits<UnqualifiedType>::ReferredType >::result ||
				    isFunctionPointer };
    enum { isStdUnsignedInt = tl::IndexOf<StdUnsignedInts, UnqualifiedType>::value >= 0 ||
			      tl::IndexOf<StdUnsignedInts, 
				    typename ReferenceTraits<UnqualifiedType>::ReferredType>::value >= 0};
    enum { isStdSignedInt   = tl::IndexOf<StdSignedInts, UnqualifiedType>::value >= 0 ||
			      tl::IndexOf<StdSignedInts, 
				    typename ReferenceTraits<UnqualifiedType>::ReferredType>::value >= 0};
    enum { isStdIntegral    = isStdUnsignedInt || isStdSignedInt ||
			      tl::IndexOf<StdOtherInts, UnqualifiedType>::value >= 0 ||
			      tl::IndexOf<StdOtherInts, 
				    typename ReferenceTraits<UnqualifiedType>::ReferredType>::value >= 0};
    enum { isStdFloat       = tl::IndexOf<StdFloats, UnqualifiedType>::value >= 0 ||
			      tl::IndexOf<StdFloats, 
				    typename ReferenceTraits<UnqualifiedType>::ReferredType>::value >= 0};
    enum { isStdArith       = isStdIntegral || isStdFloat };
    enum { isStdFundamental = isStdArith || isStdFloat || Conversion<T, void>::sameType };
	
    enum { isUnsignedInt    = isStdUnsignedInt };
    enum { isSignedInt      = isStdSignedInt };
    enum { isIntegral       = isStdIntegral || isUnsignedInt || isSignedInt };
    enum { isFloat          = isStdFloat };
    enum { isArith          = isIntegral || isFloat };
    enum { isFundamental    = isStdFundamental || isArith };
    
    typedef typename Select<isStdArith || isPointer || isMemberPointer, T, 
	    typename AddParameterType<T>::Result>::Result 
	ParameterType;
};

} // namespace tm
} // namespace ustl

#endif
