1 // Copyright 2021 gRPC 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 // http://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 GRPC_SRC_CORE_LIB_GPRPP_TABLE_H 16 #define GRPC_SRC_CORE_LIB_GPRPP_TABLE_H 17 18 #include <grpc/support/port_platform.h> 19 20 #include <stddef.h> 21 22 #include <initializer_list> 23 #include <new> 24 #include <type_traits> 25 #include <utility> 26 27 #include "absl/meta/type_traits.h" 28 #include "absl/utility/utility.h" 29 30 #include "src/core/lib/gprpp/bitset.h" 31 32 namespace grpc_core { 33 34 // Meta-programming detail types to aid in building up a Table 35 namespace table_detail { 36 37 // A tuple-like type that contains manually constructed elements. 38 template <typename... Ts> 39 struct Elements; 40 41 template <typename T, typename... Ts> 42 struct Elements<T, Ts...> : Elements<Ts...> { 43 union U { 44 U() {} 45 ~U() {} 46 GPR_NO_UNIQUE_ADDRESS T x; 47 }; 48 U u; 49 }; 50 template <> 51 struct Elements<> {}; 52 53 // Element accessor for Elements<> 54 // Provides a static method f that returns a pointer to the value of element I 55 // for Elements<Ts...> 56 template <size_t I, typename... Ts> 57 struct GetElem; 58 59 template <typename T, typename... Ts> 60 struct GetElem<0, T, Ts...> { 61 static T* f(Elements<T, Ts...>* e) { return &e->u.x; } 62 static const T* f(const Elements<T, Ts...>* e) { return &e->u.x; } 63 }; 64 65 template <size_t I, typename T, typename... Ts> 66 struct GetElem<I, T, Ts...> { 67 static auto f(Elements<T, Ts...>* e) 68 -> decltype(GetElem<I - 1, Ts...>::f(e)) { 69 return GetElem<I - 1, Ts...>::f(e); 70 } 71 static auto f(const Elements<T, Ts...>* e) 72 -> decltype(GetElem<I - 1, Ts...>::f(e)) { 73 return GetElem<I - 1, Ts...>::f(e); 74 } 75 }; 76 77 // CountIncludedStruct is the backing for the CountIncluded function below. 78 // Sets a member constant N to the number of times Needle is in Haystack. 79 template <typename Needle, typename... Haystack> 80 struct CountIncludedStruct; 81 82 template <typename Needle, typename Straw, typename... RestOfHaystack> 83 struct CountIncludedStruct<Needle, Straw, RestOfHaystack...> { 84 static constexpr size_t N = 85 static_cast<size_t>(std::is_same<Needle, Straw>::value) + 86 CountIncludedStruct<Needle, RestOfHaystack...>::N; 87 }; 88 template <typename Needle> 89 struct CountIncludedStruct<Needle> { 90 static constexpr size_t N = 0; 91 }; 92 // Returns the number of times Needle is in Haystack. 93 template <typename Needle, typename... Haystack> 94 constexpr size_t CountIncluded() { 95 return CountIncludedStruct<Needle, Haystack...>::N; 96 } 97 98 // IndexOfStruct is the backing for IndexOf below. 99 // Set a member constant N to the index of Needle in Haystack. 100 // Ignored should be void always, and is used for enable_if_t. 101 template <typename Ignored, typename Needle, typename... Haystack> 102 struct IndexOfStruct; 103 104 template <typename Needle, typename Straw, typename... RestOfHaystack> 105 struct IndexOfStruct<absl::enable_if_t<std::is_same<Needle, Straw>::value>, 106 Needle, Straw, RestOfHaystack...> { 107 // The first element is the one we're looking for. Done. 108 static constexpr size_t N = 0; 109 }; 110 template <typename Needle, typename Straw, typename... RestOfHaystack> 111 struct IndexOfStruct<absl::enable_if_t<!std::is_same<Needle, Straw>::value>, 112 Needle, Straw, RestOfHaystack...> { 113 // The first element is not the one we're looking for, recurse looking at the 114 // tail, and sum the number of recursions. 115 static constexpr size_t N = 116 1 + IndexOfStruct<void, Needle, RestOfHaystack...>::N; 117 }; 118 // Return the index of Needle in Haystack. 119 // Guarded by CountIncluded to ensure that the return type is unambiguous. 120 // If you got here from a compiler error using Table, it's likely that you've 121 // used the type-based accessor/mutators, but the type you're using is repeated 122 // more than once in the Table type arguments. Consider either using the indexed 123 // accessor/mutator variants, or eliminating the ambiguity in type resolution. 124 template <typename Needle, typename... Haystack> 125 constexpr absl::enable_if_t<CountIncluded<Needle, Haystack...>() == 1, size_t> 126 IndexOf() { 127 return IndexOfStruct<void, Needle, Haystack...>::N; 128 } 129 130 // TypeIndexStruct is the backing for TypeIndex below. 131 // Sets member type Type to the type at index I in Ts. 132 // Implemented as a simple type recursion. 133 template <size_t I, typename... Ts> 134 struct TypeIndexStruct; 135 136 template <typename T, typename... Ts> 137 struct TypeIndexStruct<0, T, Ts...> { 138 using Type = T; 139 }; 140 template <size_t I, typename T, typename... Ts> 141 struct TypeIndexStruct<I, T, Ts...> : TypeIndexStruct<I - 1, Ts...> {}; 142 // TypeIndex is the type at index I in Ts. 143 template <size_t I, typename... Ts> 144 using TypeIndex = typename TypeIndexStruct<I, Ts...>::Type; 145 146 // Helper to call the destructor of p if p is non-null. 147 template <typename T> 148 void DestructIfNotNull(T* p) { 149 if (p) p->~T(); 150 } 151 152 // Helper function... just ignore the initializer list passed into it. 153 // Allows doing 'statements' via parameter pack expansion in C++11 - given 154 // template <typename... Ts>: 155 // do_these_things({(foo<Ts>(), 1)}); 156 // will execute foo<T>() for each T in Ts. 157 // In this example we also leverage the comma operator to make the resultant 158 // type of each statement be a consistant int so that C++ type deduction works 159 // as we'd like (note that in the expression (a, 1) in C++, the 'result' of the 160 // expression is the value after the right-most ',' -- in this case 1, with a 161 // executed as a side effect. 162 template <typename T> 163 void do_these_things(std::initializer_list<T>) {} 164 165 } // namespace table_detail 166 167 // A Table<Ts> is much like a tuple<optional<Ts>...> - a set of values that are 168 // optionally present. Table efficiently packs the presence bits for size, and 169 // provides a slightly more convenient interface. 170 template <typename... Ts> 171 class Table { 172 // Helper - TypeIndex<I> is the type at index I in Ts 173 template <size_t I> 174 using TypeIndex = table_detail::TypeIndex<I, Ts...>; 175 176 public: 177 // Construct a table with no values set. 178 Table() = default; 179 // Destruct - forwards to the Destruct member with an integer sequence so we 180 // can destruct field-wise. 181 ~Table() { Destruct(absl::make_index_sequence<sizeof...(Ts)>()); } 182 183 // Copy another table 184 Table(const Table& rhs) { 185 // Since we know all fields are clear initially, pass false for or_clear. 186 Copy<false>(absl::make_index_sequence<sizeof...(Ts)>(), rhs); 187 } 188 189 // Copy another table 190 Table& operator=(const Table& rhs) { 191 // Since we may not be all clear, pass true for or_clear to have Copy() 192 // clear newly emptied fields. 193 Copy<true>(absl::make_index_sequence<sizeof...(Ts)>(), rhs); 194 return *this; 195 } 196 197 // Move from another table 198 Table(Table&& rhs) noexcept { 199 // Since we know all fields are clear initially, pass false for or_clear. 200 Move<false>(absl::make_index_sequence<sizeof...(Ts)>(), 201 std::forward<Table>(rhs)); 202 } 203 204 // Move from another table 205 Table& operator=(Table&& rhs) noexcept { 206 // Since we may not be all clear, pass true for or_clear to have Move() 207 // clear newly emptied fields. 208 Move<true>(absl::make_index_sequence<sizeof...(Ts)>(), 209 std::forward<Table>(rhs)); 210 return *this; 211 } 212 213 // Check if this table has a value for type T. 214 // Only available if there exists only one T in Ts. 215 template <typename T> 216 bool has() const { 217 return has<index_of<T>()>(); 218 } 219 220 // Check if this table has index I. 221 template <size_t I> 222 absl::enable_if_t<(I < sizeof...(Ts)), bool> has() const { 223 return present_bits_.is_set(I); 224 } 225 226 // Return the value for type T, or nullptr if it is un-set. 227 // Only available if there exists only one T in Ts. 228 template <typename T> 229 T* get() { 230 return get<index_of<T>()>(); 231 } 232 233 // Return the value for type T, or nullptr if it is un-set. 234 // Only available if there exists only one T in Ts. 235 template <typename T> 236 const T* get() const { 237 return get<index_of<T>()>(); 238 } 239 240 // Return the value for index I, or nullptr if it is un-set. 241 template <size_t I> 242 TypeIndex<I>* get() { 243 if (has<I>()) return element_ptr<I>(); 244 return nullptr; 245 } 246 247 // Return the value for index I, or nullptr if it is un-set. 248 template <size_t I> 249 const TypeIndex<I>* get() const { 250 if (has<I>()) return element_ptr<I>(); 251 return nullptr; 252 } 253 254 // Return the value for type T, default constructing it if it is un-set. 255 template <typename T> 256 T* get_or_create() { 257 return get_or_create<index_of<T>()>(); 258 } 259 260 // Return the value for index I, default constructing it if it is un-set. 261 template <size_t I> 262 TypeIndex<I>* get_or_create() { 263 auto* p = element_ptr<I>(); 264 if (!set_present<I>(true)) { 265 new (p) TypeIndex<I>(); 266 } 267 return element_ptr<I>(); 268 } 269 270 // Set the value for type T - using Args as construction arguments. 271 template <typename T, typename... Args> 272 T* set(Args&&... args) { 273 return set<index_of<T>()>(std::forward<Args>(args)...); 274 } 275 276 // Set the value for index I - using Args as construction arguments. 277 template <size_t I, typename... Args> 278 TypeIndex<I>* set(Args&&... args) { 279 auto* p = element_ptr<I>(); 280 if (set_present<I>(true)) { 281 TypeIndex<I> replacement(std::forward<Args>(args)...); 282 *p = std::move(replacement); 283 } else { 284 new (p) TypeIndex<I>(std::forward<Args>(args)...); 285 } 286 return p; 287 } 288 289 template <size_t I> 290 TypeIndex<I>* set(TypeIndex<I>&& value) { 291 auto* p = element_ptr<I>(); 292 if (set_present<I>(true)) { 293 *p = std::forward<TypeIndex<I>>(value); 294 } else { 295 new (p) TypeIndex<I>(std::forward<TypeIndex<I>>(value)); 296 } 297 return p; 298 } 299 300 // Clear the value for type T, leaving it un-set. 301 template <typename T> 302 void clear() { 303 clear<index_of<T>()>(); 304 } 305 306 // Clear the value for index I, leaving it un-set. 307 template <size_t I> 308 void clear() { 309 if (set_present<I>(false)) { 310 using T = TypeIndex<I>; 311 element_ptr<I>()->~T(); 312 } 313 } 314 315 // Iterate through each set field in the table 316 template <typename F> 317 void ForEach(F f) const { 318 ForEachImpl(std::move(f), absl::make_index_sequence<sizeof...(Ts)>()); 319 } 320 321 // Iterate through each set field in the table if it exists in Vs, in the 322 // order of Vs. 323 template <typename F, typename... Vs> 324 void ForEachIn(F f) const { 325 ForEachImpl(std::move(f), 326 absl::index_sequence<table_detail::IndexOf<Vs, Ts...>()...>()); 327 } 328 329 // Count the number of set fields in the table 330 size_t count() const { return present_bits_.count(); } 331 332 // Check if the table is completely empty 333 bool empty() const { return present_bits_.none(); } 334 335 // Clear all elements in the table. 336 void ClearAll() { ClearAllImpl(absl::make_index_sequence<sizeof...(Ts)>()); } 337 338 private: 339 // Bit field for which elements of the table are set (true) or un-set (false, 340 // the default) -- one bit for each type in Ts. 341 using PresentBits = BitSet<sizeof...(Ts)>; 342 // The tuple-like backing structure for Table. 343 using Elements = table_detail::Elements<Ts...>; 344 // Extractor from Elements 345 template <size_t I> 346 using GetElem = table_detail::GetElem<I, Ts...>; 347 348 // Given a T, return the unambiguous index of it within Ts. 349 template <typename T> 350 static constexpr size_t index_of() { 351 return table_detail::IndexOf<T, Ts...>(); 352 } 353 354 // Given an index, return a point to the (maybe uninitialized!) data value at 355 // index I. 356 template <size_t I> 357 TypeIndex<I>* element_ptr() { 358 return GetElem<I>::f(&elements_); 359 } 360 361 // Given an index, return a point to the (maybe uninitialized!) data value at 362 // index I. 363 template <size_t I> 364 const TypeIndex<I>* element_ptr() const { 365 return GetElem<I>::f(&elements_); 366 } 367 368 // Set the present bit to value (if true - value is present/set, if false, 369 // value is un-set). Returns the old value so that calling code can note 370 // transition edges. 371 template <size_t I> 372 bool set_present(bool value) { 373 bool out = present_bits_.is_set(I); 374 present_bits_.set(I, value); 375 return out; 376 } 377 378 // Set the value of index I to the value held in rhs index I if it is set. 379 // If it is unset, if or_clear is true, then clear our value, otherwise do 380 // nothing. 381 template <bool or_clear, size_t I> 382 void CopyIf(const Table& rhs) { 383 if (auto* p = rhs.get<I>()) { 384 set<I>(*p); 385 } else if (or_clear) { 386 clear<I>(); 387 } 388 } 389 390 // Set the value of index I to the value moved from rhs index I if it was set. 391 // If it is unset, if or_clear is true, then clear our value, otherwise do 392 // nothing. 393 template <bool or_clear, size_t I> 394 void MoveIf(Table&& rhs) { 395 if (auto* p = rhs.get<I>()) { 396 set<I>(std::move(*p)); 397 } else if (or_clear) { 398 clear<I>(); 399 } 400 } 401 402 // Call (*f)(value) if that value is in the table. 403 template <size_t I, typename F> 404 void CallIf(F* f) const { 405 if (auto* p = get<I>()) { 406 (*f)(*p); 407 } 408 } 409 410 // For each field (element I=0, 1, ...) if that field is present, call its 411 // destructor. 412 template <size_t... I> 413 void Destruct(absl::index_sequence<I...>) { 414 table_detail::do_these_things<int>( 415 {(table_detail::DestructIfNotNull(get<I>()), 1)...}); 416 } 417 418 // For each field (element I=0, 1, ...) copy that field into this table - 419 // or_clear as per CopyIf(). 420 template <bool or_clear, size_t... I> 421 void Copy(absl::index_sequence<I...>, const Table& rhs) { 422 table_detail::do_these_things<int>({(CopyIf<or_clear, I>(rhs), 1)...}); 423 } 424 425 // For each field (element I=0, 1, ...) move that field into this table - 426 // or_clear as per MoveIf(). 427 template <bool or_clear, size_t... I> 428 void Move(absl::index_sequence<I...>, Table&& rhs) { 429 table_detail::do_these_things<int>( 430 {(MoveIf<or_clear, I>(std::forward<Table>(rhs)), 1)...}); 431 } 432 433 // For each field (element I=0, 1, ...) if that field is present, call f. 434 template <typename F, size_t... I> 435 void ForEachImpl(F f, absl::index_sequence<I...>) const { 436 table_detail::do_these_things<int>({(CallIf<I>(&f), 1)...}); 437 } 438 439 template <size_t... I> 440 void ClearAllImpl(absl::index_sequence<I...>) { 441 table_detail::do_these_things<int>({(clear<I>(), 1)...}); 442 } 443 444 // Bit field indicating which elements are set. 445 GPR_NO_UNIQUE_ADDRESS PresentBits present_bits_; 446 // The memory to store the elements themselves. 447 GPR_NO_UNIQUE_ADDRESS Elements elements_; 448 }; 449 450 } // namespace grpc_core 451 452 #endif // GRPC_SRC_CORE_LIB_GPRPP_TABLE_H 453