/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////


#pragma once

#include <EABase/eabase.h>

#include <limits.h>


namespace eastl
{

	///////////////////////////////////////////////////////////////////////
	// add_const
	//
	// Add const to a type.
	//
	// Tor a given type T, add_const<T>::type is equivalent to T 
	// const if is_const<T>::value == false, and
	//    - is_void<T>::value == true, or
	//    - is_object<T>::value == true.
	//
	// Otherwise, add_const<T>::type is equivalent to T.
	//
	///////////////////////////////////////////////////////////////////////

	#define EASTL_TYPE_TRAIT_add_const_CONFORMANCE 1    // add_const is conforming.   

	template <typename T, bool = eastl::is_const<T>::value || eastl::is_reference<T>::value || eastl::is_function<T>::value>
	struct add_const_helper
		{ typedef T type; };

	template <typename T>
	struct add_const_helper<T, false>
		{ typedef const T type; };

	template <typename T>
	struct  add_const
		{ typedef typename eastl::add_const_helper<T>::type type; };
	
	// add_const_t is the C++17 using typedef for typename add_const<T>::type.
	// We provide a backwards-compatible means to access it through a macro for pre-C++11 compilers.
	#if defined(EA_COMPILER_NO_TEMPLATE_ALIASES)
		#define EASTL_ADD_CONST_T(T) typename add_const<T>::type
	#else
		template <typename T>
		using add_const_t = typename add_const<T>::type;
		#define EASTL_ADD_CONST_T(T) add_const_t<T>
	#endif


	///////////////////////////////////////////////////////////////////////
	// add_volatile
	//
	// Add volatile to a type.
	// 
	// For a given type T, add_volatile<T>::type is equivalent to T volatile 
	// if is_volatile<T>::value == false, and
	//   - is_void<T>::value == true, or
	//   - is_object<T>::value == true.
	//
	// Otherwise, add_volatile<T>::type is equivalent to T.
	//
	///////////////////////////////////////////////////////////////////////

	#define EASTL_TYPE_TRAIT_add_volatile_CONFORMANCE 1    // add_volatile is conforming.

	template <typename T, bool = eastl::is_volatile<T>::value || eastl::is_reference<T>::value || eastl::is_function<T>::value>
	struct add_volatile_helper
		{ typedef T type; };

	template <typename T>
	struct add_volatile_helper<T, false>
		{ typedef volatile T type; };

	template <typename T> struct add_volatile
		{ typedef typename eastl::add_volatile_helper<T>::type type; };

	template <class T> using add_volatile_t = typename add_volatile<T>::type;


    ///////////////////////////////////////////////////////////////////////
	// add_cv
	//
	// The add_cv transformation trait adds const and volatile qualification 
	// to the type to which it is applied. For a given type T, 
	// add_volatile<T>::type is equivalent to add_const<add_volatile<T>::type>::type.
	//
	///////////////////////////////////////////////////////////////////////

	#define EASTL_TYPE_TRAIT_add_cv_CONFORMANCE 1    // add_cv is conforming.

	template<typename T>
	struct add_cv
	{
		typedef typename add_const<typename add_volatile<T>::type>::type type;
	};

	template <class T> using add_cv_t = typename add_cv<T>::type;


    ///////////////////////////////////////////////////////////////////////
	// make_signed
	//
	// Used to convert an integral type to its signed equivalent, if not already.
	// T shall be a (possibly const and/or volatile-qualified) integral type 
	// or enumeration but not a bool type.;
	//
	// The user can define their own make_signed overrides for their own 
	// types by making a template specialization like done below and adding
	// it to the user's code.
	///////////////////////////////////////////////////////////////////////

	// To do: This implementation needs to be updated to support C++11 conformance (recognition of enums) and 
	// to support volatile-qualified types. It will probably be useful to have it fail for unsupported types.
	#define EASTL_TYPE_TRAIT_make_signed_CONFORMANCE 0    // make_signed is only partially conforming.

	template <typename T> struct make_signed { typedef T type; };

	template <> struct make_signed<unsigned char>            { typedef signed char            type; };
	template <> struct make_signed<const unsigned char>      { typedef const signed char      type; };
	template <> struct make_signed<unsigned short>           { typedef signed short           type; };
	template <> struct make_signed<const unsigned short>     { typedef const signed short     type; };
	template <> struct make_signed<unsigned int>             { typedef signed int             type; };
	template <> struct make_signed<const unsigned int>       { typedef const signed int       type; };
	template <> struct make_signed<unsigned long>            { typedef signed long            type; };
	template <> struct make_signed<const unsigned long>      { typedef const signed long      type; };
	template <> struct make_signed<unsigned long long>       { typedef signed long long       type; };
	template <> struct make_signed<const unsigned long long> { typedef const signed long long type; };

	#if (defined(CHAR_MAX) && defined(UCHAR_MAX) && (CHAR_MAX == UCHAR_MAX)) // If char is unsigned, we convert char to signed char. However, if char is signed then make_signed returns char itself and not signed char.
		template <> struct make_signed<char>                 { typedef signed char            type; };
		template <> struct make_signed<const char>           { typedef signed char            type; };
	#endif

	#ifndef EA_WCHAR_T_NON_NATIVE // If wchar_t is a native type instead of simply a define to an existing type...
		#if (defined(__WCHAR_MAX__) && (__WCHAR_MAX__ == 4294967295U)) // If wchar_t is a 32 bit unsigned value...
			template<>
			struct make_signed<wchar_t>
			{ typedef int32_t type; };
		#elif (defined(__WCHAR_MAX__) && (__WCHAR_MAX__ == 65535))     // If wchar_t is a 16 bit unsigned value...
			template<>
			struct make_signed<wchar_t>
			{ typedef int16_t type; };
		#elif (defined(__WCHAR_MAX__) && (__WCHAR_MAX__ == 255))       // If wchar_t is an 8 bit unsigned value...
			template<>
			struct make_signed<wchar_t>
			{ typedef int8_t type; };
		#endif
	#endif

	#if EASTL_VARIABLE_TEMPLATES_ENABLED
		template <class T>
		using make_signed_t = typename make_signed<T>::type;
	#endif


	///////////////////////////////////////////////////////////////////////
	// add_signed
	//
	// This is not a C++11 type trait, and is here for backwards compatibility
	// only. Use the C++11 make_unsigned type trait instead.
	///////////////////////////////////////////////////////////////////////

	template<class T>
	struct add_signed : public make_signed<T>
	{ typedef typename eastl::make_signed<T>::type type; };




	///////////////////////////////////////////////////////////////////////
	// make_unsigned
	//
	// Used to convert an integral type to its signed equivalent, if not already.
	// T shall be a (possibly const and/or volatile-qualified) integral type 
	// or enumeration but not a bool type.;
	//
	// The user can define their own make_signed overrides for their own 
	// types by making a template specialization like done below and adding
	// it to the user's code.
	///////////////////////////////////////////////////////////////////////

	// To do: This implementation needs to be updated to support C++11 conformance (recognition of enums) and 
	// to support volatile-qualified types. It will probably be useful to have it fail for unsupported types.
	#define EASTL_TYPE_TRAIT_make_unsigned_CONFORMANCE 0    // make_unsigned is only partially conforming.

	template <typename T> struct make_unsigned { typedef T type; };

	template <> struct make_unsigned<signed char>            { typedef unsigned char            type; };
	template <> struct make_unsigned<const signed char>      { typedef const unsigned char      type; };
	template <> struct make_unsigned<signed short>           { typedef unsigned short           type; };
	template <> struct make_unsigned<const signed short>     { typedef const unsigned short     type; };
	template <> struct make_unsigned<signed int>             { typedef unsigned int             type; };
	template <> struct make_unsigned<const signed int>       { typedef const unsigned int       type; };
	template <> struct make_unsigned<signed long>            { typedef unsigned long            type; };
	template <> struct make_unsigned<const signed long>      { typedef const unsigned long      type; };
	template <> struct make_unsigned<signed long long>       { typedef unsigned long long       type; };
	template <> struct make_unsigned<const signed long long> { typedef const unsigned long long type; };

	#if (CHAR_MIN < 0) // If char is signed, we convert char to unsigned char. However, if char is unsigned then make_unsigned returns char itself and not unsigned char.
		template <> struct make_unsigned<char>                 { typedef unsigned char          type; };
		template <> struct make_unsigned<const char>           { typedef unsigned char          type; };
	#endif

	#ifndef EA_WCHAR_T_NON_NATIVE // If wchar_t is a native type instead of simply a define to an existing type...
		#if (defined(__WCHAR_MAX__) && (__WCHAR_MAX__ != 4294967295U)) // If wchar_t is a 32 bit signed value...
			template<>
			struct make_unsigned<wchar_t>
			{ typedef uint32_t type; };
		#elif (defined(__WCHAR_MAX__) && (__WCHAR_MAX__ != 65535))     // If wchar_t is a 16 bit signed value...
			template<>
			struct make_unsigned<wchar_t>
			{ typedef uint16_t type; };
		#elif (defined(__WCHAR_MAX__) && (__WCHAR_MAX__ != 255))       // If wchar_t is an 8 bit signed value...
			template<>
			struct make_unsigned<wchar_t>
			{ typedef uint8_t type; };
		#endif
	#endif

	#if EASTL_VARIABLE_TEMPLATES_ENABLED
		template <class T>
		using make_unsigned_t = typename make_unsigned<T>::type;
	#endif



	///////////////////////////////////////////////////////////////////////
	// add_unsigned
	//
	// This is not a C++11 type trait, and is here for backwards compatibility
	// only. Use the C++11 make_unsigned type trait instead.
	// 
	// Adds unsigned-ness to the given type. 
	// Modifies only integral values; has no effect on others.
	// add_unsigned<int>::type is unsigned int
	// add_unsigned<unsigned int>::type is unsigned int
	//
	///////////////////////////////////////////////////////////////////////

	template<class T>
	struct add_unsigned : public make_unsigned<T>
	{ typedef typename eastl::make_signed<T>::type type; };



	///////////////////////////////////////////////////////////////////////
	// remove_pointer
	//
	// Remove pointer from a type.
	//
	// The remove_pointer transformation trait removes top-level indirection 
	// by pointer (if any) from the type to which it is applied. Pointers to 
	// members are not affected. For a given type T, remove_pointer<T*>::type 
	// is equivalent to T.
	//
	///////////////////////////////////////////////////////////////////////

	#define EASTL_TYPE_TRAIT_remove_pointer_CONFORMANCE 1

	template<typename T> struct remove_pointer                    { typedef T type; };
	template<typename T> struct remove_pointer<T*>                { typedef T type; };
	template<typename T> struct remove_pointer<T* const>          { typedef T type; };
	template<typename T> struct remove_pointer<T* volatile>       { typedef T type; };
	template<typename T> struct remove_pointer<T* const volatile> { typedef T type; };

	#if EASTL_VARIABLE_TEMPLATES_ENABLED
		template <class T>
		using remove_pointer_t = typename remove_pointer<T>::type;
    #endif


	///////////////////////////////////////////////////////////////////////
	// add_pointer
	//
	// Add pointer to a type.
	// Provides the member typedef type which is the type T*. If T is a 
	// reference type, then type is a pointer to the referred type. 
	//
	///////////////////////////////////////////////////////////////////////

	#define EASTL_TYPE_TRAIT_add_pointer_CONFORMANCE 1

	template<class T>
	struct add_pointer { typedef typename eastl::remove_reference<T>::type* type; };

	#if EASTL_VARIABLE_TEMPLATES_ENABLED
		template <class T>
		using add_pointer_t = typename add_pointer<T>::type;
    #endif



	///////////////////////////////////////////////////////////////////////
	// remove_extent
	//
	// The remove_extent transformation trait removes a dimension from an array.
	// For a given non-array type T, remove_extent<T>::type is equivalent to T.
	// For a given array type T[N], remove_extent<T[N]>::type is equivalent to T.
	// For a given array type const T[N], remove_extent<const T[N]>::type is equivalent to const T.
	// For example, given a multi-dimensional array type T[M][N], remove_extent<T[M][N]>::type is equivalent to T[N].
	///////////////////////////////////////////////////////////////////////

	#define EASTL_TYPE_TRAIT_remove_extent_CONFORMANCE 1    // remove_extent is conforming.

	template<class T>           struct remove_extent       { typedef T type; };
	template<class T>           struct remove_extent<T[]>  { typedef T type; };
	template<class T, size_t N> struct remove_extent<T[N]> { typedef T type; };

	#if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES)
		template <typename T>
		using remove_extent_t = typename remove_extent<T>::type;
	#endif


	///////////////////////////////////////////////////////////////////////
	// remove_all_extents
	//
	// The remove_all_extents transformation trait removes all dimensions from an array.
	// For a given non-array type T, remove_all_extents<T>::type is equivalent to T.
	// For a given array type T[N], remove_all_extents<T[N]>::type is equivalent to T.
	// For a given array type const T[N], remove_all_extents<const T[N]>::type is equivalent to const T.
	// For example, given a multi-dimensional array type T[M][N], remove_all_extents<T[M][N]>::type is equivalent to T.
	///////////////////////////////////////////////////////////////////////

	#define EASTL_TYPE_TRAIT_remove_all_extents_CONFORMANCE 1    // remove_all_extents is conforming.

	template<typename T>           struct remove_all_extents       { typedef T type; };
	template<typename T, size_t N> struct remove_all_extents<T[N]> { typedef typename eastl::remove_all_extents<T>::type type; };
	template<typename T>           struct remove_all_extents<T[]>  { typedef typename eastl::remove_all_extents<T>::type type; };

	#if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES)
		template <typename T>
		using remove_all_extents_t = typename remove_all_extents<T>::type;
	#endif



	///////////////////////////////////////////////////////////////////////
	// aligned_storage
	//
	// The aligned_storage transformation trait provides a type that is 
	// suitably aligned to store an object whose size is does not exceed length 
	// and whose alignment is a divisor of alignment. When using aligned_storage, 
	// length must be non-zero, and alignment must >= alignment_of<T>::value 
	// for some type T. We require the alignment value to be a power-of-two.
	//
	// GCC versions prior to 4.4 don't properly support this with stack-based
	// variables. The EABase EA_ALIGN_MAX_AUTOMATIC define identifies the 
	// extent to which stack (automatic) variables can be aligned for the 
	// given compiler/platform combination.
	//
	// Example usage:
	//     aligned_storage<sizeof(Widget), alignment_of(Widget)>::type widget;
	//     Widget* pWidget = new(&widget) Widget;
	//
	//     aligned_storage<sizeof(Widget), 64>::type widgetAlignedTo64;
	//     Widget* pWidget = new(&widgetAlignedTo64) Widget;
	//
	//     aligned_storage<sizeof(Widget), alignment_of(Widget)>::type widgetArray[37];
	//     Widget* pWidgetArray = new(widgetArray) Widget[37];
	///////////////////////////////////////////////////////////////////////

	#define EASTL_TYPE_TRAIT_aligned_storage_CONFORMANCE 1    // aligned_storage is conforming.

	#if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4008)
		// New versions of GCC do not support using 'alignas' with a value greater than 128.
		// However, this code using the GNU standard alignment attribute works properly.
		template<size_t N, size_t Align = EASTL_ALIGN_OF(double)>
		struct aligned_storage
		{
			struct type { unsigned char mCharData[N]; } EA_ALIGN(Align);
		};
	#elif (EABASE_VERSION_N >= 20040) && !defined(EA_COMPILER_NO_ALIGNAS) // If C++11 alignas is supported...
		template<size_t N, size_t Align = EASTL_ALIGN_OF(double)>
		struct aligned_storage
		{
			typedef struct {
				alignas(Align) unsigned char mCharData[N];
			} type;
		};

	#elif defined(EA_COMPILER_MSVC) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION < 4007)) || defined(EA_COMPILER_EDG) // At some point GCC fixed their attribute(align) to support non-literals, though it's not clear what version aside from being no later than 4.7 and no earlier than 4.2.
		// Some compilers don't allow you to to use EA_ALIGNED with anything by a numeric literal, 
		// so we can't use the simpler code like we do further below for other compilers. We support
		// only up to so much of an alignment value here.
		template<size_t N, size_t Align>
		struct aligned_storage_helper { struct type{ unsigned char mCharData[N]; }; };

		template<size_t N> struct aligned_storage_helper<N,    2> { struct EA_ALIGN(   2) type{ unsigned char mCharData[N]; }; };
		template<size_t N> struct aligned_storage_helper<N,    4> { struct EA_ALIGN(   4) type{ unsigned char mCharData[N]; }; };
		template<size_t N> struct aligned_storage_helper<N,    8> { struct EA_ALIGN(   8) type{ unsigned char mCharData[N]; }; };
		template<size_t N> struct aligned_storage_helper<N,   16> { struct EA_ALIGN(  16) type{ unsigned char mCharData[N]; }; };
		template<size_t N> struct aligned_storage_helper<N,   32> { struct EA_ALIGN(  32) type{ unsigned char mCharData[N]; }; };
		template<size_t N> struct aligned_storage_helper<N,   64> { struct EA_ALIGN(  64) type{ unsigned char mCharData[N]; }; };
		template<size_t N> struct aligned_storage_helper<N,  128> { struct EA_ALIGN( 128) type{ unsigned char mCharData[N]; }; };
		template<size_t N> struct aligned_storage_helper<N,  256> { struct EA_ALIGN( 256) type{ unsigned char mCharData[N]; }; };
		template<size_t N> struct aligned_storage_helper<N,  512> { struct EA_ALIGN( 512) type{ unsigned char mCharData[N]; }; };
		template<size_t N> struct aligned_storage_helper<N, 1024> { struct EA_ALIGN(1024) type{ unsigned char mCharData[N]; }; };
		template<size_t N> struct aligned_storage_helper<N, 2048> { struct EA_ALIGN(2048) type{ unsigned char mCharData[N]; }; };
		template<size_t N> struct aligned_storage_helper<N, 4096> { struct EA_ALIGN(4096) type{ unsigned char mCharData[N]; }; };

		template<size_t N, size_t Align = EASTL_ALIGN_OF(double)>
		struct aligned_storage
		{
			typedef typename aligned_storage_helper<N, Align>::type type;
		};

	#else
		template<size_t N, size_t Align = EASTL_ALIGN_OF(double)>
		struct aligned_storage
		{
			union type
			{
				unsigned char mCharData[N];
				struct EA_ALIGN(Align) mStruct{ }; 
			};
		};
	#endif

	#if defined(EA_COMPILER_NO_TEMPLATE_ALIASES)
		#define EASTL_ALIGNED_STORAGE_T(N, Align) typename eastl::aligned_storage_t<N, Align>::type
	#else
		template <size_t N, size_t Align = EASTL_ALIGN_OF(double)>
		using aligned_storage_t = typename aligned_storage<N, Align>::type;
		#define EASTL_ALIGNED_STORAGE_T(N, Align) eastl::aligned_storage_t<N, Align>
	#endif



	///////////////////////////////////////////////////////////////////////
	// aligned_union
	//
	// The member typedef type shall be a POD type suitable for use as
	// uninitialized storage for any object whose type is listed in Types; 
	// its size shall be at least Len. The static member alignment_value 
	// shall be an integral constant of type std::size_t whose value is 
	// the strictest alignment of all types listed in Types.
	// Note that the resulting type is not a C/C++ union, but simply memory 
	// block (of pod type) that can be used to placement-new an actual 
	// C/C++ union of the types. The actual union you declare can be a non-POD union.
	//
	// Example usage:
	//     union MyUnion {
	//         char  c;
	//         int   i;
	//         float f;
	//
	//         MyUnion(float fValue) : f(fValue) {}
	//     };
	// 
	//     aligned_union<sizeof(MyUnion), char, int, float>::type myUnionStorage;
	//     MyUnion* pMyUnion = new(&myUnionStorage) MyUnion(21.4f);
	//     pMyUnion->i = 37;
	//
	///////////////////////////////////////////////////////////////////////

	#if defined(EA_COMPILER_NO_VARIADIC_TEMPLATES) || !EASTL_TYPE_TRAIT_static_max_CONFORMANCE
		#define EASTL_TYPE_TRAIT_aligned_union_CONFORMANCE 0    // aligned_union is not conforming, as it supports only a two-member unions.

		// To consider: Expand this to include more possible types. We may want to convert this to be a recursive 
		//              template instead of like below.
		template <size_t minSize, typename Type0, typename Type1 = char, typename Type2 = char, typename Type3 = char>
		struct aligned_union
		{
			static const size_t size0           = eastl::static_max<minSize, sizeof(Type0)>::value;
			static const size_t size1           = eastl::static_max<size0,   sizeof(Type1)>::value;
			static const size_t size2           = eastl::static_max<size1,   sizeof(Type2)>::value;
			static const size_t size            = eastl::static_max<size2,   sizeof(Type3)>::value;

			static const size_t alignment0      = eastl::static_max<EA_ALIGN_OF(Type0), EA_ALIGN_OF(Type1)>::value;
			static const size_t alignment1      = eastl::static_max<alignment0,         EA_ALIGN_OF(Type2)>::value;
			static const size_t alignment_value = eastl::static_max<alignment1,         EA_ALIGN_OF(Type3)>::value;

			typedef typename eastl::aligned_storage<size, alignment_value>::type type;
		};

		#if defined(EA_COMPILER_NO_TEMPLATE_ALIASES)
			// To do: define macro.
		#else
			template <size_t minSize, typename Type0, typename Type1 = char, typename Type2 = char, typename Type3 = char>
			using aligned_union_t = typename aligned_union<minSize, Type0, Type1, Type2, Type3>::type;
		#endif
	#else
		#define EASTL_TYPE_TRAIT_aligned_union_CONFORMANCE 1    // aligned_union is conforming.

		template <size_t minSize, typename Type0, typename ...TypeN>
		struct aligned_union
		{
			static const size_t size            = eastl::static_max<minSize, sizeof(Type0), sizeof(TypeN)...>::value;
			static const size_t alignment_value = eastl::static_max<EA_ALIGN_OF(Type0), EA_ALIGN_OF(TypeN)...>::value;

			typedef typename eastl::aligned_storage<size, alignment_value>::type type;
		};

		#if defined(EA_COMPILER_NO_TEMPLATE_ALIASES)
			// To do: define macro.
		#else
			template <size_t minSize, typename... TypeN>
			using aligned_union_t = typename aligned_union<minSize, TypeN...>::type;
        #endif

	#endif


	///////////////////////////////////////////////////////////////////////
	// union_cast
	//
	// Safely converts between unrelated types that have a binary equivalency.
	// This appoach is required by strictly conforming C++ compilers because
	// directly using a C or C++ cast between unrelated types is fraught with 
	// the possibility of undefined runtime behavior due to type aliasing.
	// The Source and Dest types must be POD types due to the use of a union 
	// in C++ versions prior to C++11. C++11 relaxes the definition of a POD
	// such that it allows a classes with trivial default constructors whereas 
	// previous versions did not, so beware of this when writing portable code.
	//
	// Example usage:
	//    float f32 = 1.234f;
	//    uint32_t n32 = union_cast<uint32_t>(f32);
	//
	// Example possible mis-usage:
	// The following is valid only if you are aliasing the pointer value and 
	// not what it points to. Most of the time the user intends the latter, 
	// which isn't strictly possible.
	//    Widget* pWidget = CreateWidget();
	//    Foo*    pFoo    = union_cast<Foo*>(pWidget);
	///////////////////////////////////////////////////////////////////////

	template <typename DestType, typename SourceType>
	DestType union_cast(SourceType sourceValue)
	{
		EASTL_CT_ASSERT((sizeof(DestType) == sizeof(SourceType)) && 
						(EA_ALIGN_OF(DestType) == EA_ALIGN_OF(SourceType)));               // To support differening alignments, we would need to use a memcpy-based solution or find a way to make the two union members align with each other.
		//EASTL_CT_ASSERT(is_pod<DestType>::value && is_pod<SourceType>::value);           // Disabled because we don't want to restrict what the user can do, as some compiler's definitions of is_pod aren't up to C++11 Standards.
		//EASTL_CT_ASSERT(!is_pointer<DestType>::value && !is_pointer<SourceType>::value); // Disabled because it's valid to alias pointers as long as you are aliasong the pointer value and not what it points to.

		union {
			SourceType sourceValue;
			DestType   destValue;
		} u;
		u.sourceValue = sourceValue;

		return u.destValue;
	}



	///////////////////////////////////////////////////////////////////////
	// void_t 
	//
	// Maps a sequence of any types to void.  This utility class is used in
	// template meta programming to simplify compile time reflection mechanisms
	// required by the standard library.
	//
	// http://en.cppreference.com/w/cpp/types/void_t
	//
	// Example:
	//    template <typename T, typename = void>
	//    struct is_iterable : false_type {};
	//
	//    template <typename T>
	//    struct is_iterable<T, void_t<decltype(declval<T>().begin()), 
	//                                 decltype(declval<T>().end())>> : true_type {};
	//
	///////////////////////////////////////////////////////////////////////
	#if EASTL_VARIABLE_TEMPLATES_ENABLED
		template <class...>
		using void_t = void;
	#endif


} // namespace eastl






















