Skip to content

Instantly share code, notes, and snippets.

@chengscott
Last active January 8, 2025 01:38
Show Gist options
  • Save chengscott/40e5b500ba483c607c9f99f4546dcc51 to your computer and use it in GitHub Desktop.
Save chengscott/40e5b500ba483c607c9f99f4546dcc51 to your computer and use it in GitHub Desktop.
flat_array_t and flat_tuple_t are automatically hashable type wrapper for std::array<T, N> and std::tuple<Ts...> that can be used as keys in map/unordered_map
#include <array>
#include <functional>
#include <iostream>
#include <tuple>
#include <unordered_map>
template <class T, size_t N> union flat_array_t {
using hash_t = std::tuple_element_t<
sizeof(T) - 1,
std::tuple<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>>;
hash_t hash;
std::array<T, N> data;
flat_array_t() = default;
flat_array_t(std::array<T, N> &&rhs)
: data(std::forward<std::array<T, N>>(rhs)) {}
template <typename... Ts, typename = std::enable_if_t<sizeof...(Ts) == N>>
flat_array_t(Ts &&... rhs) : data{std::forward<Ts>(rhs)...} {}
bool operator==(const flat_array_t &rhs) const { return hash == rhs.hash; }
};
template <class T, size_t N> struct std::hash<flat_array_t<T, N>> {
std::size_t operator()(const flat_array_t<T, N> &rhs) const {
return std::hash<typename flat_array_t<T, N>::hash_t>{}(rhs.hash);
}
};
int main() {
std::unordered_map<flat_array_t<uint32_t, 2>, size_t> m2;
m2[{1, 2}]++;
m2[{3, 4}]++;
m2[{1, 3}] = m2[{1, 2}] + m2[{3, 4}];
std::cout << m2[{1, 3}] << std::endl;
std::cout << m2[{{3, 4}}] << std::endl;
std::unordered_map<flat_array_t<uint16_t, 4>, size_t> m4;
m4[{1, 2, 3, 4}]++;
m4[{5, 6, 7, 8}]++;
m4[{1, 2, 5, 6}] = m4[{1, 2, 3, 4}] + m4[{5, 6, 7, 8}];
std::cout << m4[{1, 2, 5, 6}] << std::endl;
}
#include <functional>
#include <iostream>
#include <tuple>
#include <unordered_map>
template <typename... Ts> union flat_tuple_t {
using hash_t = std::tuple_element_t<
(0 + ... + sizeof(Ts)) - 1,
std::tuple<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>>;
hash_t hash;
std::tuple<Ts...> data;
flat_tuple_t() = default;
flat_tuple_t(std::tuple<Ts...> &&rhs)
: data{std::forward<std::tuple<Ts...>>(rhs)} {}
flat_tuple_t(Ts &&... rhs) : data{std::forward<Ts>(rhs)...} {}
bool operator==(const flat_tuple_t &rhs) const { return hash == rhs.hash; }
};
template <typename... Ts> struct std::hash<flat_tuple_t<Ts...>> {
std::size_t operator()(const flat_tuple_t<Ts...> &rhs) const {
return std::hash<typename flat_tuple_t<Ts...>::hash_t>{}(rhs.hash);
}
};
int main() {
std::cout << sizeof(flat_tuple_t<uint16_t, uint8_t>::hash_t) << std::endl;
std::unordered_map<flat_tuple_t<uint8_t, uint16_t>, size_t> m2;
m2[{1, 2}]++;
m2[{1, 2}]++;
m2[{3, 4}]++;
m2[{1, 3}] = m2[{1, 2}] + m2[{3, 4}];
std::cout << m2[{1, 3}] << std::endl;
std::unordered_map<flat_tuple_t<uint8_t, uint8_t, uint8_t, uint8_t>, size_t>
m4;
m4[{1, 2, 3, 4}]++;
m4[{5, 6, 7, 8}]++;
m4[{1, 2, 5, 6}] = m4[{1, 2, 3, 4}] + m4[{5, 6, 7, 8}];
std::cout << m4[{1, 2, 5, 6}] << std::endl;
}
#include <iostream>
#include <array>
#include <unordered_map>
union uint32x2_t {
using hash_t = uint64_t;
hash_t hash;
std::array<uint32_t, 2> data;
uint32x2_t() = default;
uint32x2_t(const std::array<uint32_t, 2> &rhs) : data{rhs} {}
bool operator==(const uint32x2_t &rhs) const { return hash == rhs.hash; }
};
template <> struct std::hash<uint32x2_t> {
std::size_t operator()(const uint32x2_t &rhs) const {
return std::hash<uint32x2_t::hash_t>{}(rhs.hash);
}
};
int main() {
std::unordered_map<uint32x2_t, size_t> m;
m[{{1, 2}}]++;
m[{{3, 4}}]++;
m[{{1, 3}}] = m[{{1, 2}}] + m[{{3, 4}}];
std::cout << m[{{1, 3}}] << std::endl;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment