// (c) Electronic Arts.  All Rights Reserved.
//
// EATypeOf/typeof.h
//
// Implements the EA::Type::TypeOf<T>() function, which returns a unique numeric
// identifier (of type EA::Type::TypeID) based on the (compile-time) type of T.
// This header defines the following typedef:
//
//     TypeID           // typedef for an integral type that holds type IDs
//
// and the following two versions of the TypeOf function:
//
//     TypeOf<T>()      // returns a TypeID for the type T
//     TypeOf(object)   // returns a TypeID for the type of object
//
// Note that the object form of TypeOf returns the compile-time type of the object
// and not its runtime (polymorphic) type.  If you wish to discover the runtime
// type of an object (without using C++'s RTTI facilities), you could, however,
// build yourself a system using TypeIDs as the type identifiers.
//
// As a means of uniquely identifying types in a system, TypeIDs are more efficient
// than strings and are easier to maintain and scale than enumerated lists of types.
// In order to call TypeOf<T>(), one only needs visibility to the declaration of T
// and not to its definition.  This means that TypeOf<T>() can be applied to a T
// based on only its forward declaration, which reduces dependencies and simplifies
// maintenance.
//
// TypeIDs should not be used to generate IDs for serialization.  TypeIDs are not
// consistent across platforms and are not even guaranteed to be consistent from
// build to build.
//
// David Bollo (mailto:dbollo@ea.com)
 
#ifndef EATYPEOF_TYPEOF_H
#define EATYPEOF_TYPEOF_H

#include "typeid.h"
#include "EABase/eabase.h"


// EA_TYPEOF_USE_RUNTIME_HASH
// EA_TYPEOF_USE_STATIC_HASH
// EA_TYPEOF_USE_LINK_ID
//
// Selects which ID generation method to use in TypeOf<T>().  Only one of the
// EA_TYPEOF_USE_... symbols should be defined.  If none are defined by the user
// (the typical case), then the following block of code will select the most
// appropriate one for the compiler / configuration.
//
// EA_TYPEOF_USE_RUNTIME_HASH means that the ID will be derived from a string hash
// of the function signature of TypeOf<T>.  Given that T is part of the signature,
// this will give a different ID for different Ts.
//
// EA_TYPEOF_USE_STATIC_HASH is like EA_TYPEOF_USE_RUNTIME_HASH, except that the
// hash value will be computed at compile time and optimized down to a simple
// constant load expression.  In debug configurations, EA_TYPEOF_USE_STATIC_HASH
// will automatically be converted to EA_TYPEOF_USE_RUNTIME_HASH because the static
// hash depends on the optimizer to generate good code; when the optimizer is
// disabled, the runtime hash gives better results.
//
// EA_TYPEOF_USE_LINK_ID means that the ID will be derived from a unique address
// assigned by the linker.  This method does not depend on hashing and so is not as
// dependent on a good compiler implementation, but, because it uses the linker, it
// is not suitable for DLL builds.  In particular, different DLLs will have
// differing values for TypeOf<T> and so type IDs can therefore not be passed
// between modules.
//
// Unrecognized compilers default to using EA_TYPEOF_USE_LINK_ID because it is the
// most portable, however EA_TYPEOF_USE_STATIC_HASH is the best overall technique,
// and so should be preferred so long as the compiler can correctly optimize the
// hash generation down to a constant load expression.
//
#if !defined(EA_TYPEOF_USE_RUNTIME_HASH) && !defined(EA_TYPEOF_USE_STATIC_HASH) && !defined(EA_TYPEOF_USE_LINK_ID)

#if defined(EA_COMPILER_MSVC)
	#define EA_TYPEOF_USE_STATIC_HASH
#elif defined(EA_COMPILER_SN)
	// SN's compiler incorrectly implements __PRETTY_FUNCTION__ as a const char*
	// rather than as a const char[], and so we cannot use static hash
	#define EA_TYPEOF_USE_RUNTIME_HASH
#elif defined(EA_COMPILER_GNUC)
	#define EA_TYPEOF_USE_STATIC_HASH
#else
	#define EA_TYPEOF_USE_LINK_ID
#endif

#if defined(EA_TYPEOF_USE_STATIC_HASH) && (defined(EA_DEBUG) || defined(_DEBUG))
	#undef  EA_TYPEOF_USE_STATIC_HASH
	#define EA_TYPEOF_USE_RUNTIME_HASH
#endif

#endif

#if defined(EA_TYPEOF_USE_RUNTIME_HASH) + defined(EA_TYPEOF_USE_STATIC_HASH) + defined(EA_TYPEOF_USE_LINK_ID) > 1
	#error Multiple EA_TYPEOF_USE_... definitions detected:  Please #define only one of EA_TYPEOF_USE_RUNTIME_HASH, EA_TYPEOF_USE_STATIC_HASH or EA_TYPEOF_USE_LINK_ID
#endif

#if defined(EA_TYPEOF_USE_LINK_ID) && defined(EA_DLL)
	#error EA_TYPEOF_USE_LINK_ID is not supported for DLL builds:  Please switch to either EA_TYPEOF_USE_RUNTIME_HASH or EA_TYPEOF_USE_STATIC_HASH
#endif


// EA_TYPEOF_FUNCTION_SIGNATURE
//
// Specifies the identifier to use to retrieve a unique function-signature string
// that includes the names of template arguments for template functions.  C99 has
// a similar identifier __func__ that:
//
//     shall be implicitly declared by the translator as if, immediately following
//     the opening brace of each function definition, the declaration
//
//         static const char __func__[] = "function-name";
//
//     appeared, where function-name is the name of the lexically-enclosing function
//
// however, this name is typically the unadorned (ie: excluding type arguments) name
// of the function and so is not useful for these circumstances.
//
// Fortunately, most C++ compilers also declare an identifier that does include the
// function's type signature as well as its name.
//
// This identifier is only used by the hashing implementations of TypeOf and so is
// only defined if one of EA_TYPEOF_USE_RUNTIME_HASH or EA_TYPEOF_USE_STATIC_HASH
// is set.
//
#if defined(EA_TYPEOF_USE_RUNTIME_HASH) || defined(EA_TYPEOF_USE_STATIC_HASH)

#if defined(EA_COMPILER_MSVC)
	#define EA_TYPEOF_FUNCTION_SIGNATURE		__FUNCDNAME__
#elif defined(EA_COMPILER_SN)
	#define EA_TYPEOF_FUNCTION_SIGNATURE		__PRETTY_FUNCTION__
#elif defined(EA_COMPILER_GNUC)
	#define EA_TYPEOF_FUNCTION_SIGNATURE		__PRETTY_FUNCTION__
#else
	#error Unknown compiler:  Please #define EA_TYPEOF_FUNCTION_SIGNATURE for this compiler
#endif

#endif


#if defined(EA_TYPEOF_USE_RUNTIME_HASH) || defined(EA_TYPEOF_USE_STATIC_HASH)
	#include "EATypeOf/internal/hash.h"
#else
	#include "EATypeOf/internal/linkid.h"
#endif


namespace EA		{
namespace Type		{

// TypeOf
//
// Returns the TypeID of a given type.  There are two overloads:
//
//     TypeOf<T>()      // returns a TypeID for the type T
//     TypeOf(object)   // returns a TypeID for the type of object
//
// In the first case, the TypeID is returned for the specified T (which only needs
// to be declared, not necessarily defined).  In the second case, the TypeID is
// returned for the (compile-time) type of the object.
//
// References are ignored, as are top-level const and volatile qualifiers.  In
// other words:
//
//     int i, j;
//     int& ref = j;
//
//     TypeOf(i) == TypeOf(ref);                // references are ignored
//     TypeOf<int>() == TypeOf<const int>();    // top-level const is ignored
//     TypeOf<int>() == TypeOf<volatile int>(); // top-level volatile is ignored
//
// Pointers are still considered distinct types, however:
//
//     TypeOf<int>() != TypeOf<int*>();         // pointers are distinct
//
// TypeOf<T>() can be called for a type T even if that type T has only been declared
// and not defined.  In other words, the following code is legal and will give a
// valid and consistent ID for all Dogs, even if no definition for Dog is ever
// provided:
//
//     class Dog;                               // declaration only
//     TypeID id = TypeOf<Dog>();               // ok
//
template <typename T> EA_FORCE_INLINE TypeID TypeOf_Impl()
{
#if defined(EA_TYPEOF_USE_RUNTIME_HASH)
	static TypeID id;
	if (!id)
	{
		id = internal::RuntimeHash<TypeID>(EA_TYPEOF_FUNCTION_SIGNATURE);
	}
	return id;
#elif defined(EA_TYPEOF_USE_STATIC_HASH)
	return internal::StaticHash<TypeID>(EA_TYPEOF_FUNCTION_SIGNATURE);
#else
	return static_cast<TypeID>(internal::LinkID<T>::ID());
#endif
}

template <typename T> EA_FORCE_INLINE TypeID TypeOf()
{
	return TypeOf_Impl<const volatile T>();
}

template <typename T> EA_FORCE_INLINE TypeID TypeOf(const volatile T&)
{
	return TypeOf_Impl<const volatile T>();
}


}
}


#endif
