Просмотр исходного кода

Add EnumClassHash to hash enum types

Unless it is specialized, std::hash<T> is not (really) supported if T is
an enum, in which case we can now use 'EnumClassHash' instead.

EnumClassHash is used in hash_combine(), as well as in BsStdHeaders.h
for unordered data structures.  This commit also effectively reverts
commit 410359d7, which specialized std::hash for an enum type.
Marc Legendre 9 лет назад
Родитель
Сommit
e80b15d955

+ 1 - 16
Source/BansheeCore/Include/BsGpuProgram.h

@@ -224,19 +224,4 @@ namespace BansheeEngine
 	};
 
 	/** @} */
-}
-
-namespace std
-{
-/** Hash value generator for GpuProgramProfile. */
-template<>
-struct hash<BansheeEngine::GpuProgramProfile>
-{
-	size_t operator()(const BansheeEngine::GpuProgramProfile& profile) const
-	{
-		size_t hash = 0;
-		BansheeEngine::hash_combine(hash, (int)profile);
-		return hash;
-	}
-};
-}
+}

+ 25 - 0
Source/BansheeUtility/Include/BsEnumClassHash.h

@@ -0,0 +1,25 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+namespace BansheeEngine
+{
+	/**
+	 * Hash for enum types, to be used instead of std::hash<T> when T is an enum.
+	 *
+	 * Until C++14, std::hash<T> is not defined if T is a enum (see
+	 * http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2148).  But
+	 * even with C++14, as of october 2016, std::hash for enums is not widely
+	 * implemented by compilers, so here when T is a enum, we use EnumClassHash
+	 * instead of std::hash. (For instance, in BansheeEngine::hash_combine(), or
+	 * BansheeEngine::UnorderedMap.)
+	 */
+	struct EnumClassHash
+	{
+		template <typename T>
+		std::size_t operator()(T t) const
+		{
+			return static_cast<std::size_t>(t);
+		}
+	};
+}

+ 8 - 3
Source/BansheeUtility/Include/BsStdHeaders.h

@@ -77,6 +77,8 @@ extern "C" {
 }
 #endif
 
+#include <BsEnumClassHash.h>
+
 namespace BansheeEngine
 {
 	/** @addtogroup Containers
@@ -115,19 +117,22 @@ namespace BansheeEngine
 	template <typename K, typename V, typename P = std::less<K>, typename A = StdAlloc<std::pair<const K, V>>> 
 	using MultiMap = std::multimap<K, V, P, A>;
 
+	template <typename Key>
+	using HashType = typename std::conditional<std::is_enum<Key>::value, EnumClassHash, std::hash<Key>>::type;
+
 	/** An associative container containing an unordered set of elements. Usually faster than Set for larger data sets. */
-	template <typename T, typename H = std::hash<T>, typename C = std::equal_to<T>, typename A = StdAlloc<T>> 
+	template <typename T, typename H = HashType<T>, typename C = std::equal_to<T>, typename A = StdAlloc<T>> 
 	using UnorderedSet = std::unordered_set<T, H, C, A>;
 
 	/** An associative container containing an ordered set of key-value pairs. Usually faster than Map for larger data sets. */
-	template <typename K, typename V, typename H = std::hash<K>, typename C = std::equal_to<K>, typename A = StdAlloc<std::pair<const K, V>>> 
+	template <typename K, typename V, typename H = HashType<K>, typename C = std::equal_to<K>, typename A = StdAlloc<std::pair<const K, V>>> 
 	using UnorderedMap = std::unordered_map<K, V, H, C, A>;
 
 	/** 
 	 * An associative container containing an ordered set of key-value pairs where multiple elements can have the same key.
 	 * Usually faster than MultiMap for larger data sets.
 	 */
-	template <typename K, typename V, typename H = std::hash<K>, typename C = std::equal_to<K>, typename A = StdAlloc<std::pair<const K, V>>> 
+	template <typename K, typename V, typename H = HashType<K>, typename C = std::equal_to<K>, typename A = StdAlloc<std::pair<const K, V>>> 
 	using UnorderedMultimap = std::unordered_multimap<K, V, H, C, A>;
 
 	/** @} */

+ 6 - 2
Source/BansheeUtility/Include/BsUtil.h

@@ -2,6 +2,8 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 
+#include <BsEnumClassHash.h>
+
 namespace BansheeEngine
 {
 	/** @addtogroup General
@@ -12,7 +14,9 @@ namespace BansheeEngine
 	template <class T>
 	inline void hash_combine(std::size_t& seed, const T& v)
 	{
-		std::hash<T> hasher;
+		using HashType = typename std::conditional<std::is_enum<T>::value, EnumClassHash, std::hash<T>>::type;
+		HashType hasher;
+		// std::hash<T> hasher;
 		seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
 	}
 
@@ -23,4 +27,4 @@ namespace BansheeEngine
 	String BS_UTILITY_EXPORT md5(const String& source);
 
 	/** @} */
-}
+}