Given my prototype for a simple hashing method:
template <typename _iterator>
constexpr uint32_t hash_impl(_iterator buf, size_t len);
Generating constexpr
hashes for simple strings is pretty trivial:
template <char const* str>
constexpr uint32_t generate()
{
constexpr std::string_view strView = str;
return hash_impl(str, strView.size());
}
constexpr uint32_t generate(const std::string_view& str)
{
return hash_impl(str.begin(), str.size());
}
constexpr static char str1[] = "Hello World!";
constexpr crc32::hash hash1 = crc32::generate<str1>();
constexpr std::string_view str2("Hello World!");
constexpr crc32::hash hash2 = crc32::generate(str2);
I'd also like to generate constexpr
hashes for a variety of simple (POD and trivial structs) types too. However, I'm not sure how to get the byte representation of these types in a constexpr
-friendly way.
My naive implementation:
template <typename T, typename = std::enable_if_t< std::is_standard_layout_v<T> && !std::is_pointer_v<T> >>
constexpr uint32_t generate(const T& value)
{
return hash_impl(reinterpret_cast<const std::byte*>(&value), sizeof(T));
}
falls over because &value
and reinterpret_cast<>
breaks constexpr rules. I've searched for a workaround, but other answers on the site indicate that it's not possible.
Worst case scenario, I can manually check for and hash specific types as so:
template <typename T, typename = std::enable_if_t< std::is_standard_layout_v<T> && !std::is_pointer_v<T> >>
constexpr uint32_t generate(const T& value)
{
if constexpr (std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>)
{
char buf[] =
{
static_cast<char>(value >> 0),
static_cast<char>(value >> 8),
static_cast<char>(value >> 16),
static_cast<char>(value >> 24)
};
return generate(buf, 4);
}
else if constexpr (/* etc... */)
// ...
}
but this falls over as soon as I try to implement this for something like a float (which has no bitwise operators) or for trivial custom types (eg, struct foo { int a; };
unless I write an extra code block for foo
).
I feel like I must be overlooking something simple, but I can't find any stl utility or think of any fancy template trick that would suit. Perhaps there's something in C 20 or C 23 that I'm not aware of?
CodePudding user response:
It is not possible in C 17. In C 20 you can use std::bit_cast
to get the object representation of a trivially copyable type:
auto object_representation = std::bit_cast<std::array<std::byte, sizeof(T)>>(value);
(Technically you can argue about whether or not it is guaranteed that std::array
has no additional padding/members that would make this ill-formed, but in practice that is not a concern.)
You can then pass the array as a pointer/size pair to the hash implementation. You should probably also add
static_assert(std::has_unique_object_representations_v<T>);
If the assertion fails you will have no guarantee that objects with same value will have the same object representation and same hash.
Btw. std::is_standard_layout_v
is not the property you need here. The property that you need to verify is std::is_trivially_copyable_v
. Being standard-layout is neither sufficient nor necessary to be able to inspect and use the object representation like this.