#227 Hash function for enums (#581)

* #227 Hash function for enums

* Move enum hash definition to bottom, so gcc doesn't complain about it

* Explicitly specify etl hash

Co-authored-by: Jesse <jli@planarmotor.com>
This commit is contained in:
Jesse Li 2022-09-01 11:10:35 -07:00 committed by GitHub
parent 5aa14f85aa
commit a38a8fc33c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 108 additions and 0 deletions

View File

@ -86,13 +86,40 @@ namespace etl
return fnv_1a_64(begin, end);
}
#endif
//*************************************************************************
/// Primary definition of base hash class, by default is poisoned
//*************************************************************************
template<typename T, bool IsEnum=false>
struct hash_base {
private:
hash_base(); // can't default construct
hash_base(const hash_base& other); // can't copy construct
hash_base& operator=(const hash_base& other); // can't copy assign
#if ETL_USING_CPP11
hash_base(hash_base&& other); // can't move construct
hash_base& operator=(hash_base&& other); // can't move assign
#endif
};
// Specialization for enums depends on definitions for integers, so it comes later
}
#if ETL_USING_CPP11
//***************************************************************************
/// Generic declaration for etl::hash, including default for enums
///\ingroup hash
//***************************************************************************
template <typename T>
struct hash : private_hash::hash_base<T, etl::is_enum<T>::value>{};
#else
//***************************************************************************
/// Generic declaration for etl::hash
///\ingroup hash
//***************************************************************************
template <typename T> struct hash;
#endif
//***************************************************************************
/// Specialisation for bool.
@ -436,6 +463,23 @@ namespace etl
}
}
};
namespace private_hash {
//*************************************************************************
/// Specialization for enums
//*************************************************************************
template<typename T>
struct hash_base<T, true>{
size_t operator()(T v) const
{
if (sizeof(size_t) >= sizeof(T)) {
return static_cast<size_t>(v);
} else {
return ::etl::hash<unsigned long long>{}(static_cast<unsigned long long>(v));
}
}
};
}
}
#endif // ETL_USING_8BIT_TYPES

View File

@ -28,6 +28,7 @@ SOFTWARE.
#include "unit_test_framework.h"
#include <type_traits>
#include <iterator>
#include <string>
#include <vector>
@ -35,8 +36,21 @@ SOFTWARE.
#include "etl/hash.h"
// for testing user-defined hash specializations
namespace { class CustomType{}; }
namespace etl
{
template <>
struct hash<CustomType>
{
size_t operator()(CustomType) {return 0;}
};
}
namespace
{
SUITE(test_hash)
{
//*************************************************************************
@ -213,6 +227,56 @@ namespace
CHECK_EQUAL(size_t(&i), hash);
}
//*************************************************************************
TEST(test_hash_enums)
{
enum class MyEnumClass : char {
OneE = 0x1E
};
enum MyEnum : char {
MyEnum_TwoF = 0x2F
};
size_t hash = etl::hash<MyEnumClass>()(MyEnumClass::OneE);
CHECK_EQUAL(static_cast<size_t>(MyEnumClass::OneE), hash);
hash = etl::hash<MyEnum>()(MyEnum_TwoF);
CHECK_EQUAL(0x2F, hash);
}
TEST(test_hash_big_enums) {
constexpr unsigned long long big_number = 0x5AA555AA3CC333CCULL;
enum class MyBigEnumClass : unsigned long long {
Big = big_number
};
size_t hash = etl::hash<MyBigEnumClass>()(MyBigEnumClass::Big);
size_t expectedHash = etl::hash<unsigned long long>()(big_number);
CHECK_EQUAL(expectedHash, hash);
}
TEST(test_hash_poisoned) {
// Unspecialized hash<> should be disabled (unusable) - see https://en.cppreference.com/w/cpp/utility/hash
class A {};
typedef etl::hash<A> general_hasher;
CHECK_FALSE(std::is_default_constructible<general_hasher>::value);
CHECK_FALSE(std::is_copy_constructible<general_hasher>::value);
CHECK_FALSE(std::is_move_constructible<general_hasher>::value);
CHECK_FALSE(std::is_copy_assignable<general_hasher>::value);
CHECK_FALSE(std::is_move_assignable<general_hasher>::value);
}
TEST(test_hash_custom) {
typedef etl::hash<CustomType> custom_hasher;
CHECK_TRUE(std::is_default_constructible<custom_hasher>::value);
CHECK_TRUE(std::is_copy_constructible<custom_hasher>::value);
CHECK_TRUE(std::is_move_constructible<custom_hasher>::value);
CHECK_TRUE(std::is_copy_assignable<custom_hasher>::value);
CHECK_TRUE(std::is_move_assignable<custom_hasher>::value);
}
};
}