userver: userver/utils/impl/transparent_hash.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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