1 // Copyright 2018 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ 16 #define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ 17 18 #include <cstddef> 19 #include <memory> 20 #include <new> 21 #include <type_traits> 22 #include <utility> 23 24 #include "absl/meta/type_traits.h" 25 26 namespace absl { 27 ABSL_NAMESPACE_BEGIN 28 namespace container_internal { 29 30 // Defines how slots are initialized/destroyed/moved. 31 template <class Policy, class = void> 32 struct hash_policy_traits { 33 // The type of the keys stored in the hashtable. 34 using key_type = typename Policy::key_type; 35 36 private: 37 struct ReturnKey { 38 // When C++17 is available, we can use std::launder to provide mutable 39 // access to the key for use in node handle. 40 #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 41 template <class Key, 42 absl::enable_if_t<std::is_lvalue_reference<Key>::value, int> = 0> Implhash_policy_traits::ReturnKey43 static key_type& Impl(Key&& k, int) { 44 return *std::launder( 45 const_cast<key_type*>(std::addressof(std::forward<Key>(k)))); 46 } 47 #endif 48 49 template <class Key> Implhash_policy_traits::ReturnKey50 static Key Impl(Key&& k, char) { 51 return std::forward<Key>(k); 52 } 53 54 // When Key=T&, we forward the lvalue reference. 55 // When Key=T, we return by value to avoid a dangling reference. 56 // eg, for string_hash_map. 57 template <class Key, class... Args> 58 auto operator()(Key&& k, const Args&...) const 59 -> decltype(Impl(std::forward<Key>(k), 0)) { 60 return Impl(std::forward<Key>(k), 0); 61 } 62 }; 63 64 template <class P = Policy, class = void> 65 struct ConstantIteratorsImpl : std::false_type {}; 66 67 template <class P> 68 struct ConstantIteratorsImpl<P, absl::void_t<typename P::constant_iterators>> 69 : P::constant_iterators {}; 70 71 public: 72 // The actual object stored in the hash table. 73 using slot_type = typename Policy::slot_type; 74 75 // The argument type for insertions into the hashtable. This is different 76 // from value_type for increased performance. See initializer_list constructor 77 // and insert() member functions for more details. 78 using init_type = typename Policy::init_type; 79 80 using reference = decltype(Policy::element(std::declval<slot_type*>())); 81 using pointer = typename std::remove_reference<reference>::type*; 82 using value_type = typename std::remove_reference<reference>::type; 83 84 // Policies can set this variable to tell raw_hash_set that all iterators 85 // should be constant, even `iterator`. This is useful for set-like 86 // containers. 87 // Defaults to false if not provided by the policy. 88 using constant_iterators = ConstantIteratorsImpl<>; 89 90 // PRECONDITION: `slot` is UNINITIALIZED 91 // POSTCONDITION: `slot` is INITIALIZED 92 template <class Alloc, class... Args> 93 static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { 94 Policy::construct(alloc, slot, std::forward<Args>(args)...); 95 } 96 97 // PRECONDITION: `slot` is INITIALIZED 98 // POSTCONDITION: `slot` is UNINITIALIZED 99 template <class Alloc> 100 static void destroy(Alloc* alloc, slot_type* slot) { 101 Policy::destroy(alloc, slot); 102 } 103 104 // Transfers the `old_slot` to `new_slot`. Any memory allocated by the 105 // allocator inside `old_slot` to `new_slot` can be transferred. 106 // 107 // OPTIONAL: defaults to: 108 // 109 // clone(new_slot, std::move(*old_slot)); 110 // destroy(old_slot); 111 // 112 // PRECONDITION: `new_slot` is UNINITIALIZED and `old_slot` is INITIALIZED 113 // POSTCONDITION: `new_slot` is INITIALIZED and `old_slot` is 114 // UNINITIALIZED 115 template <class Alloc> 116 static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) { 117 transfer_impl(alloc, new_slot, old_slot, 0); 118 } 119 120 // PRECONDITION: `slot` is INITIALIZED 121 // POSTCONDITION: `slot` is INITIALIZED 122 template <class P = Policy> 123 static auto element(slot_type* slot) -> decltype(P::element(slot)) { 124 return P::element(slot); 125 } 126 127 // Returns the amount of memory owned by `slot`, exclusive of `sizeof(*slot)`. 128 // 129 // If `slot` is nullptr, returns the constant amount of memory owned by any 130 // full slot or -1 if slots own variable amounts of memory. 131 // 132 // PRECONDITION: `slot` is INITIALIZED or nullptr 133 template <class P = Policy> 134 static size_t space_used(const slot_type* slot) { 135 return P::space_used(slot); 136 } 137 138 // Provides generalized access to the key for elements, both for elements in 139 // the table and for elements that have not yet been inserted (or even 140 // constructed). We would like an API that allows us to say: `key(args...)` 141 // but we cannot do that for all cases, so we use this more general API that 142 // can be used for many things, including the following: 143 // 144 // - Given an element in a table, get its key. 145 // - Given an element initializer, get its key. 146 // - Given `emplace()` arguments, get the element key. 147 // 148 // Implementations of this must adhere to a very strict technical 149 // specification around aliasing and consuming arguments: 150 // 151 // Let `value_type` be the result type of `element()` without ref- and 152 // cv-qualifiers. The first argument is a functor, the rest are constructor 153 // arguments for `value_type`. Returns `std::forward<F>(f)(k, xs...)`, where 154 // `k` is the element key, and `xs...` are the new constructor arguments for 155 // `value_type`. It's allowed for `k` to alias `xs...`, and for both to alias 156 // `ts...`. The key won't be touched once `xs...` are used to construct an 157 // element; `ts...` won't be touched at all, which allows `apply()` to consume 158 // any rvalues among them. 159 // 160 // If `value_type` is constructible from `Ts&&...`, `Policy::apply()` must not 161 // trigger a hard compile error unless it originates from `f`. In other words, 162 // `Policy::apply()` must be SFINAE-friendly. If `value_type` is not 163 // constructible from `Ts&&...`, either SFINAE or a hard compile error is OK. 164 // 165 // If `Ts...` is `[cv] value_type[&]` or `[cv] init_type[&]`, 166 // `Policy::apply()` must work. A compile error is not allowed, SFINAE or not. 167 template <class F, class... Ts, class P = Policy> 168 static auto apply(F&& f, Ts&&... ts) 169 -> decltype(P::apply(std::forward<F>(f), std::forward<Ts>(ts)...)) { 170 return P::apply(std::forward<F>(f), std::forward<Ts>(ts)...); 171 } 172 173 // Returns the "key" portion of the slot. 174 // Used for node handle manipulation. 175 template <class P = Policy> 176 static auto mutable_key(slot_type* slot) 177 -> decltype(P::apply(ReturnKey(), element(slot))) { 178 return P::apply(ReturnKey(), element(slot)); 179 } 180 181 // Returns the "value" (as opposed to the "key") portion of the element. Used 182 // by maps to implement `operator[]`, `at()` and `insert_or_assign()`. 183 template <class T, class P = Policy> 184 static auto value(T* elem) -> decltype(P::value(elem)) { 185 return P::value(elem); 186 } 187 188 private: 189 // Use auto -> decltype as an enabler. 190 template <class Alloc, class P = Policy> 191 static auto transfer_impl(Alloc* alloc, slot_type* new_slot, 192 slot_type* old_slot, int) 193 -> decltype((void)P::transfer(alloc, new_slot, old_slot)) { 194 P::transfer(alloc, new_slot, old_slot); 195 } 196 template <class Alloc> 197 static void transfer_impl(Alloc* alloc, slot_type* new_slot, 198 slot_type* old_slot, char) { 199 construct(alloc, new_slot, std::move(element(old_slot))); 200 destroy(alloc, old_slot); 201 } 202 }; 203 204 } // namespace container_internal 205 ABSL_NAMESPACE_END 206 } // namespace absl 207 208 #endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ 209