// Copyright 2021 gRPC authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef GRPC_SRC_CORE_UTIL_TABLE_H #define GRPC_SRC_CORE_UTIL_TABLE_H #include #include #include #include #include #include #include "absl/meta/type_traits.h" #include "absl/utility/utility.h" #include "src/core/util/bitset.h" namespace grpc_core { // Meta-programming detail types to aid in building up a Table namespace table_detail { // A tuple-like type that contains manually constructed elements. template struct Elements; template struct Elements::value>, T, Ts...> : Elements { struct alignas(T) Data { unsigned char bytes[sizeof(T)]; }; Data x; Elements() {} T* ptr() { return reinterpret_cast(x.bytes); } const T* ptr() const { return reinterpret_cast(x.bytes); } }; template struct Elements::value>, T, Ts...> : Elements { T* ptr() { return reinterpret_cast(this); } const T* ptr() const { return reinterpret_cast(this); } }; template <> struct Elements {}; // Element accessor for Elements<> // Provides a static method f that returns a pointer to the value of element I // for Elements template struct GetElem; template struct GetElem<0, T, Ts...> { static T* f(Elements* e) { return e->ptr(); } static const T* f(const Elements* e) { return e->ptr(); } }; template struct GetElem { static auto f(Elements* e) -> decltype(GetElem::f(e)) { return GetElem::f(e); } static auto f(const Elements* e) -> decltype(GetElem::f(e)) { return GetElem::f(e); } }; // CountIncludedStruct is the backing for the CountIncluded function below. // Sets a member constant N to the number of times Needle is in Haystack. template struct CountIncludedStruct; template struct CountIncludedStruct { static constexpr size_t N = static_cast(std::is_same::value) + CountIncludedStruct::N; }; template struct CountIncludedStruct { static constexpr size_t N = 0; }; // Returns the number of times Needle is in Haystack. template constexpr size_t CountIncluded() { return CountIncludedStruct::N; } // IndexOfStruct is the backing for IndexOf below. // Set a member constant N to the index of Needle in Haystack. // Ignored should be void always, and is used for enable_if_t. template struct IndexOfStruct; template struct IndexOfStruct::value>, Needle, Straw, RestOfHaystack...> { // The first element is the one we're looking for. Done. static constexpr size_t N = 0; }; template struct IndexOfStruct::value>, Needle, Straw, RestOfHaystack...> { // The first element is not the one we're looking for, recurse looking at the // tail, and sum the number of recursions. static constexpr size_t N = 1 + IndexOfStruct::N; }; // Return the index of Needle in Haystack. // Guarded by CountIncluded to ensure that the return type is unambiguous. // If you got here from a compiler error using Table, it's likely that you've // used the type-based accessor/mutators, but the type you're using is repeated // more than once in the Table type arguments. Consider either using the indexed // accessor/mutator variants, or eliminating the ambiguity in type resolution. template constexpr absl::enable_if_t() == 1, size_t> IndexOf() { return IndexOfStruct::N; } // TypeIndexStruct is the backing for TypeIndex below. // Sets member type Type to the type at index I in Ts. // Implemented as a simple type recursion. template struct TypeIndexStruct; template struct TypeIndexStruct<0, T, Ts...> { using Type = T; }; template struct TypeIndexStruct : TypeIndexStruct {}; // TypeIndex is the type at index I in Ts. template using TypeIndex = typename TypeIndexStruct::Type; // Helper to call the destructor of p if p is non-null. template void DestructIfNotNull(T* p) { if (p) p->~T(); } // Helper function... just ignore the initializer list passed into it. // Allows doing 'statements' via parameter pack expansion in C++11 - given // template : // do_these_things({(foo(), 1)}); // will execute foo() for each T in Ts. // In this example we also leverage the comma operator to make the resultant // type of each statement be a consistent int so that C++ type deduction works // as we'd like (note that in the expression (a, 1) in C++, the 'result' of the // expression is the value after the right-most ',' -- in this case 1, with a // executed as a side effect. template void do_these_things(std::initializer_list) {} } // namespace table_detail // A Table is much like a tuple...> - a set of values that are // optionally present. Table efficiently packs the presence bits for size, and // provides a slightly more convenient interface. template class Table { // Helper - TypeIndex is the type at index I in Ts template using TypeIndex = table_detail::TypeIndex; public: // Construct a table with no values set. Table() = default; // Destruct - forwards to the Destruct member with an integer sequence so we // can destruct field-wise. ~Table() { Destruct(absl::make_index_sequence()); } // Copy another table Table(const Table& rhs) { // Since we know all fields are clear initially, pass false for or_clear. Copy(absl::make_index_sequence(), rhs); } // Copy another table Table& operator=(const Table& rhs) { // Since we may not be all clear, pass true for or_clear to have Copy() // clear newly emptied fields. Copy(absl::make_index_sequence(), rhs); return *this; } // Move from another table Table(Table&& rhs) noexcept { // Since we know all fields are clear initially, pass false for or_clear. Move(absl::make_index_sequence(), std::forward(rhs)); } // Move from another table Table& operator=(Table&& rhs) noexcept { // Since we may not be all clear, pass true for or_clear to have Move() // clear newly emptied fields. Move(absl::make_index_sequence(), std::forward
(rhs)); return *this; } // Check if this table has a value for type T. // Only available if there exists only one T in Ts. template bool has() const { return has()>(); } // Check if this table has index I. template absl::enable_if_t<(I < sizeof...(Ts)), bool> has() const { return present_bits_.is_set(I); } // Return the value for type T, or nullptr if it is un-set. // Only available if there exists only one T in Ts. template T* get() { return get()>(); } // Return the value for type T, or nullptr if it is un-set. // Only available if there exists only one T in Ts. template const T* get() const { return get()>(); } // Return the value for index I, or nullptr if it is un-set. template TypeIndex* get() { if (has()) return element_ptr(); return nullptr; } // Return the value for index I, or nullptr if it is un-set. template const TypeIndex* get() const { if (has()) return element_ptr(); return nullptr; } // Return the value for type T, default constructing it if it is un-set. template T* get_or_create() { return get_or_create()>(); } // Return the value for index I, default constructing it if it is un-set. template TypeIndex* get_or_create() { auto* p = element_ptr(); if (!set_present(true)) { new (p) TypeIndex(); } return element_ptr(); } // Set the value for type T - using Args as construction arguments. template T* set(Args&&... args) { return set()>(std::forward(args)...); } // Set the value for index I - using Args as construction arguments. template TypeIndex* set(Args&&... args) { auto* p = element_ptr(); if (set_present(true)) { TypeIndex replacement(std::forward(args)...); *p = std::move(replacement); } else { new (p) TypeIndex(std::forward(args)...); } return p; } template TypeIndex* set(TypeIndex&& value) { auto* p = element_ptr(); if (set_present(true)) { *p = std::forward>(value); } else { new (p) TypeIndex(std::forward>(value)); } return p; } // Clear the value for type T, leaving it un-set. template void clear() { clear()>(); } // Clear the value for index I, leaving it un-set. template void clear() { if (set_present(false)) { using T = TypeIndex; element_ptr()->~T(); } } // Iterate through each set field in the table template void ForEach(F f) const { ForEachImpl(std::move(f), absl::make_index_sequence()); } // Iterate through each set field in the table if it exists in Vs, in the // order of Vs. template void ForEachIn(F f) const { ForEachImpl(std::move(f), absl::index_sequence()...>()); } // Iterate through each set field in the table if it exists in Vs, in the // order of Vs. For each existing field, call the filter function. If the // function returns true, keep the field. Otherwise, remove the field. template void FilterIn(F f) { FilterInImpl(std::move(f), absl::index_sequence()...>()); } // Count the number of set fields in the table size_t count() const { return present_bits_.count(); } // Check if the table is completely empty bool empty() const { return present_bits_.none(); } // Clear all elements in the table. void ClearAll() { ClearAllImpl(absl::make_index_sequence()); } private: // Bit field for which elements of the table are set (true) or un-set (false, // the default) -- one bit for each type in Ts. using PresentBits = BitSet; // The tuple-like backing structure for Table. using Elements = table_detail::Elements; // Extractor from Elements template using GetElem = table_detail::GetElem; // Given a T, return the unambiguous index of it within Ts. template static constexpr size_t index_of() { return table_detail::IndexOf(); } // Given an index, return a point to the (maybe uninitialized!) data value at // index I. template TypeIndex* element_ptr() { return GetElem::f(&elements_); } // Given an index, return a point to the (maybe uninitialized!) data value at // index I. template const TypeIndex* element_ptr() const { return GetElem::f(&elements_); } // Set the present bit to value (if true - value is present/set, if false, // value is un-set). Returns the old value so that calling code can note // transition edges. template bool set_present(bool value) { bool out = present_bits_.is_set(I); present_bits_.set(I, value); return out; } // Set the value of index I to the value held in rhs index I if it is set. // If it is unset, if or_clear is true, then clear our value, otherwise do // nothing. template void CopyIf(const Table& rhs) { if (auto* p = rhs.get()) { set(*p); } else if (or_clear) { clear(); } } // Set the value of index I to the value moved from rhs index I if it was set. // If it is unset, if or_clear is true, then clear our value, otherwise do // nothing. template void MoveIf(Table&& rhs) { if (auto* p = rhs.get()) { set(std::move(*p)); } else if (or_clear) { clear(); } } // Call (*f)(value) if that value is in the table. template void CallIf(F* f) const { if (auto* p = get()) { (*f)(*p); } } // Call (*f)(value) if that value is in the table. // If the value is present in the table and (*f)(value) returns false, remove // the value from the table. template void FilterIf(F* f) { if (auto* p = get()) { if (!(*f)(*p)) { clear(); } } } // For each field (element I=0, 1, ...) if that field is present, call its // destructor. template void Destruct(absl::index_sequence) { table_detail::do_these_things( {(table_detail::DestructIfNotNull(get()), 1)...}); } // For each field (element I=0, 1, ...) copy that field into this table - // or_clear as per CopyIf(). template void Copy(absl::index_sequence, const Table& rhs) { table_detail::do_these_things({(CopyIf(rhs), 1)...}); } // For each field (element I=0, 1, ...) move that field into this table - // or_clear as per MoveIf(). template void Move(absl::index_sequence, Table&& rhs) { table_detail::do_these_things( {(MoveIf(std::forward
(rhs)), 1)...}); } // For each field (element I=0, 1, ...) if that field is present, call f. template void ForEachImpl(F f, absl::index_sequence) const { table_detail::do_these_things({(CallIf(&f), 1)...}); } // For each field (element I=0, 1, ...) if that field is present, call f. If // f returns false, remove the field from the table. template void FilterInImpl(F f, absl::index_sequence) { table_detail::do_these_things({(FilterIf(&f), 1)...}); } template void ClearAllImpl(absl::index_sequence) { table_detail::do_these_things({(clear(), 1)...}); } // Bit field indicating which elements are set. PresentBits present_bits_; // The memory to store the elements themselves. Elements elements_; }; } // namespace grpc_core #endif // GRPC_SRC_CORE_UTIL_TABLE_H