userver: userver/utils/impl/transparent_hash.hpp Source File
Loading...
Searching...
No Matches
transparent_hash.hpp
1#pragma once
2
3#include <functional>
4#include <string_view>
5#include <unordered_map>
6#include <unordered_set>
7
8#if __cpp_lib_generic_unordered_lookup < 201811L
9#include <boost/unordered_map.hpp>
10#include <boost/unordered_set.hpp>
11#endif
12
13USERVER_NAMESPACE_BEGIN
14
15namespace utils::impl {
16
17template <typename Key>
18struct TransparentHash : public std::hash<std::string_view> {
19 static_assert(std::is_convertible_v<Key, std::string_view>,
20 "TransparentHash is only implemented for strings for far");
21
22 using is_transparent [[maybe_unused]] = void;
23};
24
25// Use:
26// - std::unordered_{map,set} in C++20
27// - boost::unordered_{map,set} in C++17
28
29#if __cpp_lib_generic_unordered_lookup >= 201811L
30template <typename Key, typename Value, typename Hash = TransparentHash<Key>,
31 typename Equal = std::equal_to<>>
32using TransparentMap = std::unordered_map<Key, Value, Hash, Equal>;
33
34template <typename Key, typename Hash = TransparentHash<Key>,
35 typename Equal = std::equal_to<>>
36using TransparentSet = std::unordered_set<Key, Hash, Equal>;
37#else
38template <typename Key, typename Value, typename Hash = TransparentHash<Key>,
39 typename Equal = std::equal_to<>>
40using TransparentMap = boost::unordered_map<Key, Value, Hash, Equal>;
41
42template <typename Key, typename Hash = TransparentHash<Key>,
43 typename Equal = std::equal_to<>>
44using TransparentSet = boost::unordered_set<Key, Hash, Equal>;
45#endif
46
47template <typename TransparentContainer, typename Key>
48auto FindTransparent(TransparentContainer&& container, const Key& key) {
49 static_assert(!std::is_rvalue_reference_v<TransparentContainer>, "Dangling");
50#if __cpp_lib_generic_unordered_lookup >= 201811L
51 return container.find(key);
52#else
53 return container.find(key, container.hash_function(), container.key_eq());
54#endif
55}
56
57template <typename TransparentMap, typename Key>
58auto* FindTransparentOrNullptr(TransparentMap&& map, const Key& key) {
59 static_assert(!std::is_rvalue_reference_v<TransparentMap>, "Dangling");
60 const auto iterator = FindTransparent(map, key);
61 return iterator == map.end() ? nullptr : &iterator->second;
62}
63
64template <typename TransparentMap, typename Key, typename Value>
65void TransparentInsertOrAssign(TransparentMap& map, Key&& key, Value&& value) {
66 using StoredKey = typename TransparentMap::key_type;
67 using ForwardedKey =
68 std::conditional_t<std::is_same_v<std::decay_t<Key>, StoredKey>, Key&&,
69 StoredKey>;
70#if __cpp_lib_generic_unordered_lookup >= 201811L
71 // Still no heterogeneous support in insert_or_assign - this will result in
72 // an extra copy of 'key' if 'key' is already present. See wg21.link/P2363.
73 map.insert_or_assign(static_cast<ForwardedKey>(key),
74 std::forward<Value>(value));
75#else
76 const auto iterator = map.find(key, map.hash_function(), map.key_eq());
77 if (iterator != map.end()) {
78 iterator->second = std::forward<Value>(value);
79 } else {
80 // Performs an extra lookup. Oh well, Boost has no insert_or_assign support.
81 map.emplace(static_cast<ForwardedKey>(key), std::forward<Value>(value));
82 }
83#endif
84}
85
86} // namespace utils::impl
87
88USERVER_NAMESPACE_END