Refactor the declaration of `raw_hash_set`/`btree` to omit default template parameters from the subclasses. This reduces binary bloat by making mangled names smaller and by reducing strings like `__PRETTY_FUNCTION__.` The default types can be inferred and do not provide extra information. PiperOrigin-RevId: 844837029 Change-Id: I67f3c9445bd018ea829fa584784095e2e84cb739
diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h index 131f622..0746f72 100644 --- a/absl/container/btree_map.h +++ b/absl/container/btree_map.h
@@ -57,18 +57,44 @@ #ifndef ABSL_CONTAINER_BTREE_MAP_H_ #define ABSL_CONTAINER_BTREE_MAP_H_ +#include <functional> +#include <memory> +#include <type_traits> +#include <utility> + #include "absl/base/attributes.h" #include "absl/container/internal/btree.h" // IWYU pragma: export #include "absl/container/internal/btree_container.h" // IWYU pragma: export +#include "absl/container/internal/common.h" +#include "absl/container/internal/container_memory.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { +template <typename Key, typename Data, typename... Params> +struct map_params_impl; + +template <typename Key, typename Data> +struct btree_map_defaults { + using Compare = std::less<Key>; + using Alloc = std::allocator<std::pair<const Key, Data>>; + using TargetNodeSize = std::integral_constant<int, 256>; + using IsMulti = std::false_type; +}; + template <typename Key, typename Data, typename Compare, typename Alloc, int TargetNodeSize, bool IsMulti> -struct map_params; +using map_params = typename ApplyWithoutDefaultSuffix< + map_params_impl, + TypeList<void, void, typename btree_map_defaults<Key, Data>::Compare, + typename btree_map_defaults<Key, Data>::Alloc, + typename btree_map_defaults<Key, Data>::TargetNodeSize, + typename btree_map_defaults<Key, Data>::IsMulti>, + TypeList<Key, Data, Compare, Alloc, + std::integral_constant<int, TargetNodeSize>, + std::integral_constant<bool, IsMulti>>>::type; } // namespace container_internal @@ -855,11 +881,20 @@ // A parameters structure for holding the type parameters for a btree_map. // Compare and Alloc should be nothrow copy-constructible. -template <typename Key, typename Data, typename Compare, typename Alloc, - int TargetNodeSize, bool IsMulti> -struct map_params : common_params<Key, Compare, Alloc, TargetNodeSize, IsMulti, - /*IsMap=*/true, map_slot_policy<Key, Data>> { - using super_type = typename map_params::common_params; +template <typename Key, typename Data, typename... Params> +struct map_params_impl + : common_params< + Key, + GetFromListOr<typename btree_map_defaults<Key, Data>::Compare, 0, + Params...>, + GetFromListOr<typename btree_map_defaults<Key, Data>::Alloc, 1, + Params...>, + GetFromListOr<typename btree_map_defaults<Key, Data>::TargetNodeSize, + 2, Params...>::value, + GetFromListOr<typename btree_map_defaults<Key, Data>::IsMulti, 3, + Params...>::value, + /*IsMap=*/true, map_slot_policy<Key, Data>> { + using super_type = typename map_params_impl::common_params; using mapped_type = Data; // This type allows us to move keys when it is safe to do so. It is safe // for maps in which value_type and mutable_value_type are layout compatible. @@ -868,6 +903,21 @@ using value_type = typename super_type::value_type; using init_type = typename super_type::init_type; + static_assert( + std::is_same_v< + map_params< + Key, Data, + GetFromListOr<typename btree_map_defaults<Key, Data>::Compare, 0, + Params...>, + GetFromListOr<typename btree_map_defaults<Key, Data>::Alloc, 1, + Params...>, + GetFromListOr< + typename btree_map_defaults<Key, Data>::TargetNodeSize, 2, + Params...>::value, + GetFromListOr<typename btree_map_defaults<Key, Data>::IsMulti, 3, + Params...>::value>, + map_params_impl>); + template <typename V> static auto key(const V &value ABSL_ATTRIBUTE_LIFETIME_BOUND) -> decltype((value.first)) {
diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h index 44a39cf..991cb89 100644 --- a/absl/container/btree_set.h +++ b/absl/container/btree_set.h
@@ -56,9 +56,15 @@ #ifndef ABSL_CONTAINER_BTREE_SET_H_ #define ABSL_CONTAINER_BTREE_SET_H_ +#include <functional> +#include <memory> +#include <type_traits> +#include <utility> + #include "absl/base/attributes.h" #include "absl/container/internal/btree.h" // IWYU pragma: export #include "absl/container/internal/btree_container.h" // IWYU pragma: export +#include "absl/container/internal/common.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -68,9 +74,27 @@ template <typename Key> struct set_slot_policy; +template <typename Key, typename...Params> +struct set_params_impl; + +template <typename Key> +struct btree_set_defaults { + using Compare = std::less<Key>; + using Alloc = std::allocator<Key>; + using TargetNodeSize = std::integral_constant<int, 256>; + using IsMulti = std::false_type; +}; + template <typename Key, typename Compare, typename Alloc, int TargetNodeSize, bool IsMulti> -struct set_params; +using set_params = typename ApplyWithoutDefaultSuffix< + set_params_impl, + TypeList<void, typename btree_set_defaults<Key>::Compare, + typename btree_set_defaults<Key>::Alloc, + typename btree_set_defaults<Key>::TargetNodeSize, + typename btree_set_defaults<Key>::IsMulti>, + TypeList<Key, Compare, Alloc, std::integral_constant<int, TargetNodeSize>, + std::integral_constant<bool, IsMulti>>>::type; } // namespace container_internal @@ -803,12 +827,34 @@ // A parameters structure for holding the type parameters for a btree_set. // Compare and Alloc should be nothrow copy-constructible. -template <typename Key, typename Compare, typename Alloc, int TargetNodeSize, - bool IsMulti> -struct set_params : common_params<Key, Compare, Alloc, TargetNodeSize, IsMulti, - /*IsMap=*/false, set_slot_policy<Key>> { +template <typename Key, typename... Params> +struct set_params_impl + : common_params< + Key, + GetFromListOr<typename btree_set_defaults<Key>::Compare, 0, + Params...>, + GetFromListOr<typename btree_set_defaults<Key>::Alloc, 1, Params...>, + GetFromListOr<typename btree_set_defaults<Key>::TargetNodeSize, 2, + Params...>::value, + GetFromListOr<typename btree_set_defaults<Key>::IsMulti, 3, + Params...>::value, + /*IsMap=*/false, set_slot_policy<Key>> { using value_type = Key; - using slot_type = typename set_params::common_params::slot_type; + using slot_type = typename set_params_impl::common_params::slot_type; + + static_assert( + std::is_same_v< + set_params< + Key, + GetFromListOr<typename btree_set_defaults<Key>::Compare, 0, + Params...>, + GetFromListOr<typename btree_set_defaults<Key>::Alloc, 1, + Params...>, + GetFromListOr<typename btree_set_defaults<Key>::TargetNodeSize, 2, + Params...>::value, + GetFromListOr<typename btree_set_defaults<Key>::IsMulti, 3, + Params...>::value>, + set_params_impl>); template <typename V> static const V &key(const V &value) {
diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc index a6438f5..0cf3ed3 100644 --- a/absl/container/btree_test.cc +++ b/absl/container/btree_test.cc
@@ -3544,6 +3544,109 @@ TestBasicFunctionality(set_type()); } +// Alias whose only purpose is to have the same length as set_params for better +// alignment in the test below. +template <typename... T> +using set_p_impl = set_params_impl<T...>; + +TEST(BtreeTest, SetParamsStripsDefaults) { + using K = int; + using DA = btree_set_defaults<int>::Compare; + using DB = btree_set_defaults<int>::Alloc; + using DC = btree_set_defaults<int>::TargetNodeSize; + using DD = btree_set_defaults<int>::IsMulti; + + using XA = std::greater<int>; + struct XB {}; + using XC = std::integral_constant<int, 100>; + using XD = std::true_type; + + EXPECT_TRUE((std::is_same_v<set_params<K, XA, XB, XC{}, XD{}>, + set_p_impl<K, XA, XB, XC, XD>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, XA, XB, XC{}, DD{}>, + set_p_impl<K, XA, XB, XC>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, XA, XB, DC{}, XD{}>, + set_p_impl<K, XA, XB, DC, XD>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, XA, XB, DC{}, DD{}>, + set_p_impl<K, XA, XB>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, XA, DB, XC{}, XD{}>, + set_p_impl<K, XA, DB, XC, XD>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, XA, DB, XC{}, DD{}>, + set_p_impl<K, XA, DB, XC>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, XA, DB, DC{}, XD{}>, + set_p_impl<K, XA, DB, DC, XD>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, XA, DB, DC{}, DD{}>, + set_p_impl<K, XA>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, DA, XB, XC{}, XD{}>, + set_p_impl<K, DA, XB, XC, XD>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, DA, XB, XC{}, DD{}>, + set_p_impl<K, DA, XB, XC>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, DA, XB, DC{}, XD{}>, + set_p_impl<K, DA, XB, DC, XD>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, DA, XB, DC{}, DD{}>, + set_p_impl<K, DA, XB>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, DA, DB, XC{}, XD{}>, + set_p_impl<K, DA, DB, XC, XD>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, DA, DB, XC{}, DD{}>, + set_p_impl<K, DA, DB, XC>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, DA, DB, DC{}, XD{}>, + set_p_impl<K, DA, DB, DC, XD>>)); + EXPECT_TRUE((std::is_same_v<set_params<K, DA, DB, DC{}, DD{}>, + set_p_impl<K>>)); +} + +// Alias whose only purpose is to have the same length as map_params for better +// alignment in the test below. +template <typename... T> +using map_p_impl = map_params_impl<T...>; + +TEST(BtreeTest, MapParamsStripsDefaults) { + using K = int; + using V = double; + using DA = btree_map_defaults<int, double>::Compare; + using DB = btree_map_defaults<int, double>::Alloc; + using DC = btree_map_defaults<int, double>::TargetNodeSize; + using DD = btree_map_defaults<int, double>::IsMulti; + + using XA = std::greater<int>; + struct XB {}; + using XC = std::integral_constant<int, 100>; + using XD = std::true_type; + + EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, XB, XC{}, XD{}>, + map_p_impl<K, V, XA, XB, XC, XD>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, XB, XC{}, DD{}>, + map_p_impl<K, V, XA, XB, XC>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, XB, DC{}, XD{}>, + map_p_impl<K, V, XA, XB, DC, XD>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, XB, DC{}, DD{}>, + map_p_impl<K, V, XA, XB>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, DB, XC{}, XD{}>, + map_p_impl<K, V, XA, DB, XC, XD>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, DB, XC{}, DD{}>, + map_p_impl<K, V, XA, DB, XC>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, DB, DC{}, XD{}>, + map_p_impl<K, V, XA, DB, DC, XD>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, XA, DB, DC{}, DD{}>, + map_p_impl<K, V, XA>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, XB, XC{}, XD{}>, + map_p_impl<K, V, DA, XB, XC, XD>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, XB, XC{}, DD{}>, + map_p_impl<K, V, DA, XB, XC>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, XB, DC{}, XD{}>, + map_p_impl<K, V, DA, XB, DC, XD>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, XB, DC{}, DD{}>, + map_p_impl<K, V, DA, XB>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, DB, XC{}, XD{}>, + map_p_impl<K, V, DA, DB, XC, XD>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, DB, XC{}, DD{}>, + map_p_impl<K, V, DA, DB, XC>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, DB, DC{}, XD{}>, + map_p_impl<K, V, DA, DB, DC, XD>>)); + EXPECT_TRUE((std::is_same_v<map_params<K, V, DA, DB, DC{}, DD{}>, + map_p_impl<K, V>>)); +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END
diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h index 7d6fd21..7ce3353 100644 --- a/absl/container/flat_hash_map.h +++ b/absl/container/flat_hash_map.h
@@ -127,13 +127,17 @@ // if (result != ducks.end()) { // std::cout << "Result: " << result->second << std::endl; // } -template <class K, class V, class Hash = DefaultHashContainerHash<K>, - class Eq = DefaultHashContainerEq<K>, - class Allocator = std::allocator<std::pair<const K, V>>> +template < + class K, class V, + class Hash = + typename container_internal::FlatHashMapPolicy<K, V>::DefaultHash, + class Eq = typename container_internal::FlatHashMapPolicy<K, V>::DefaultEq, + class Allocator = + typename container_internal::FlatHashMapPolicy<K, V>::DefaultAlloc> class ABSL_ATTRIBUTE_OWNER flat_hash_map - : public absl::container_internal::raw_hash_map< + : public absl::container_internal::InstantiateRawHashMap< absl::container_internal::FlatHashMapPolicy<K, V>, Hash, Eq, - Allocator> { + Allocator>::type { using Base = typename flat_hash_map::raw_hash_map; public: @@ -637,6 +641,10 @@ using mapped_type = V; using init_type = std::pair</*non const*/ key_type, mapped_type>; + using DefaultHash = DefaultHashContainerHash<K>; + using DefaultEq = DefaultHashContainerEq<K>; + using DefaultAlloc = std::allocator<std::pair<const K, V>>; + template <class Allocator, class... Args> static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { slot_policy::construct(alloc, slot, std::forward<Args>(args)...);
diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h index 95e4533..a469fa0 100644 --- a/absl/container/flat_hash_set.h +++ b/absl/container/flat_hash_set.h
@@ -124,12 +124,16 @@ // if (ducks.contains("dewey")) { // std::cout << "We found dewey!" << std::endl; // } -template <class T, class Hash = DefaultHashContainerHash<T>, - class Eq = DefaultHashContainerEq<T>, - class Allocator = std::allocator<T>> +template < + class T, + class Hash = typename container_internal::FlatHashSetPolicy<T>::DefaultHash, + class Eq = typename container_internal::FlatHashSetPolicy<T>::DefaultEq, + class Allocator = + typename container_internal::FlatHashSetPolicy<T>::DefaultAlloc> class ABSL_ATTRIBUTE_OWNER flat_hash_set - : public absl::container_internal::raw_hash_set< - absl::container_internal::FlatHashSetPolicy<T>, Hash, Eq, Allocator> { + : public absl::container_internal::InstantiateRawHashSet< + absl::container_internal::FlatHashSetPolicy<T>, Hash, Eq, + Allocator>::type { using Base = typename flat_hash_set::raw_hash_set; public: @@ -535,6 +539,10 @@ using init_type = T; using constant_iterators = std::true_type; + using DefaultHash = DefaultHashContainerHash<T>; + using DefaultEq = DefaultHashContainerEq<T>; + using DefaultAlloc = std::allocator<T>; + template <class Allocator, class... Args> static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { absl::allocator_traits<Allocator>::construct(*alloc, slot,
diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h index 5ef6c56..3e263a3 100644 --- a/absl/container/internal/common.h +++ b/absl/container/internal/common.h
@@ -15,7 +15,10 @@ #ifndef ABSL_CONTAINER_INTERNAL_COMMON_H_ #define ABSL_CONTAINER_INTERNAL_COMMON_H_ +#include <algorithm> #include <cassert> +#include <cstddef> +#include <tuple> #include <type_traits> #include "absl/meta/type_traits.h" @@ -243,6 +246,54 @@ NodeType node; }; +// Utilities to strip redundant template parameters from the underlying +// implementation types. +// We use a variadic pack (ie Params...) to specify required prefix of types for +// non-default types, and then we use GetFromListOr to select the provided types +// or the default ones otherwise. +// +// These default types do not contribute information for debugging and just +// bloat the binary. +// Removing the redundant tail types reduces mangled names and stringified +// function names like __PRETTY_FUNCTION__. +// +// How to use: +// 1. Define a template with `typename ...Params` +// 2. Instantiate it via `ApplyWithoutDefaultSuffix<>` to only pass the minimal +// set of types. +// 3. Inside the template use `GetFromListOr` to map back from the existing +// `Params` list to the actual types, filling the gaps when types are +// missing. + +template <typename Or, size_t N, typename... Params> +using GetFromListOr = std::tuple_element_t<(std::min)(N, sizeof...(Params)), + std::tuple<Params..., Or>>; + +template <typename... T> +struct TypeList { + template <template <typename...> class Template> + using Apply = Template<T...>; +}; + +// Evaluate to `Template<TPrefix...>` where the last type in the list (if any) +// is different from the corresponding one in the default list. +// Eg +// ApplyWithoutDefaultSuffix<Template, TypeList<a, b, c>, TypeList<a, X, c>> +// evaluates to +// Template<a, X> +template <template <typename...> class Template, typename D, typename T, + typename L = TypeList<>, typename = void> +struct ApplyWithoutDefaultSuffix { + using type = typename L::template Apply<Template>; +}; +template <template <typename...> class Template, typename D, typename... Ds, + typename T, typename... Ts, typename... L> +struct ApplyWithoutDefaultSuffix< + Template, TypeList<D, Ds...>, TypeList<T, Ts...>, TypeList<L...>, + std::enable_if_t<!std::is_same_v<TypeList<D, Ds...>, TypeList<T, Ts...>>>> + : ApplyWithoutDefaultSuffix<Template, TypeList<Ds...>, TypeList<Ts...>, + TypeList<L..., T>> {}; + } // namespace container_internal ABSL_NAMESPACE_END } // namespace absl
diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h index 85adf87..9338f16 100644 --- a/absl/container/internal/raw_hash_map.h +++ b/absl/container/internal/raw_hash_map.h
@@ -31,8 +31,20 @@ ABSL_NAMESPACE_BEGIN namespace container_internal { -template <class Policy, class Hash, class Eq, class Alloc> -class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> { +template <class Policy, class... Params> +class raw_hash_map; + +template <typename Policy, typename Hash, typename Eq, typename Alloc> +struct InstantiateRawHashMap { + using type = typename ApplyWithoutDefaultSuffix< + raw_hash_map, + TypeList<int, typename Policy::DefaultHash, typename Policy::DefaultEq, + typename Policy::DefaultAlloc>, + TypeList<Policy, Hash, Eq, Alloc>>::type; +}; + +template <class Policy, class... Params> +class raw_hash_map : public raw_hash_set<Policy, Params...> { // P is Policy. It's passed as a template argument to support maps that have // incomplete types as values, as in unordered_map<K, IncompleteType>. // MappedReference<> may be a non-reference type. @@ -45,6 +57,10 @@ using MappedConstReference = decltype(P::value( std::addressof(std::declval<typename raw_hash_map::const_reference>()))); + using Hash = typename raw_hash_map::raw_hash_set::hasher; + using Eq = typename raw_hash_map::raw_hash_set::key_equal; + using Alloc = typename raw_hash_map::raw_hash_set::allocator_type; + template <class K> using key_arg = typename KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>:: @@ -327,6 +343,13 @@ } private: + static_assert( + std::is_same_v< + typename InstantiateRawHashMap<Policy, Hash, Eq, Alloc>::type, + raw_hash_map>, + "Redundant template parameters were passed. Use InstantiateRawHashMap<> " + "instead"); + template <class K, class V> std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 1d209e0..d307e2d 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h
@@ -1138,7 +1138,7 @@ HeapOrSoo heap_or_soo_; }; -template <class Policy, class Hash, class Eq, class Alloc> +template <class Policy, class... Params> class raw_hash_set; // Returns the next valid capacity after `n`. @@ -1760,32 +1760,62 @@ Group::NonIterableBitMaskType mask_empty, FindInfo target_group, absl::FunctionRef<size_t(size_t)> recompute_hash); +template <typename Policy, typename Hash, typename Eq, typename Alloc> +struct InstantiateRawHashSet { + using type = typename ApplyWithoutDefaultSuffix< + raw_hash_set, + TypeList<void, typename Policy::DefaultHash, typename Policy::DefaultEq, + typename Policy::DefaultAlloc>, + TypeList<Policy, Hash, Eq, Alloc>>::type; +}; + // A SwissTable. // // Policy: a policy defines how to perform different operations on // the slots of the hashtable (see hash_policy_traits.h for the full interface // of policy). // +// Params...: a variadic list of parameters that allows us to omit default +// types. This reduces the mangled name of the class and the size of +// debug strings like __PRETTY_FUNCTION__. Default types do not give +// any new information. +// // Hash: a (possibly polymorphic) functor that hashes keys of the hashtable. The // functor should accept a key and return size_t as hash. For best performance // it is important that the hash function provides high entropy across all bits // of the hash. +// This is the first element in `Params...` if it exists, or Policy::DefaultHash +// otherwise. // // Eq: a (possibly polymorphic) functor that compares two keys for equality. It // should accept two (of possibly different type) keys and return a bool: true // if they are equal, false if they are not. If two keys compare equal, then // their hash values as defined by Hash MUST be equal. +// This is the second element in `Params...` if it exists, or Policy::DefaultEq +// otherwise. // // Allocator: an Allocator // [https://en.cppreference.com/w/cpp/named_req/Allocator] with which // the storage of the hashtable will be allocated and the elements will be // constructed and destroyed. -template <class Policy, class Hash, class Eq, class Alloc> +// This is the third element in `Params...` if it exists, or +// Policy::DefaultAlloc otherwise. +template <class Policy, class... Params> class raw_hash_set { using PolicyTraits = hash_policy_traits<Policy>; + using Hash = GetFromListOr<typename Policy::DefaultHash, 0, Params...>; + using Eq = GetFromListOr<typename Policy::DefaultEq, 1, Params...>; + using Alloc = GetFromListOr<typename Policy::DefaultAlloc, 2, Params...>; using KeyArgImpl = KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>; + static_assert( + std::is_same_v< + typename InstantiateRawHashSet<Policy, Hash, Eq, Alloc>::type, + raw_hash_set>, + "Redundant template parameters were passed. Use InstantiateRawHashSet<> " + "instead"); + public: using init_type = typename PolicyTraits::init_type; using key_type = typename PolicyTraits::key_type; @@ -2640,8 +2670,11 @@ // Moves elements from `src` into `this`. // If the element already exists in `this`, it is left unmodified in `src`. - template <typename H, typename E> - void merge(raw_hash_set<Policy, H, E, Alloc>& src) { // NOLINT + template < + typename... Params2, + typename = std::enable_if_t<std::is_same_v< + Alloc, typename raw_hash_set<Policy, Params2...>::allocator_type>>> + void merge(raw_hash_set<Policy, Params2...>& src) { // NOLINT AssertNotDebugCapacity(); src.AssertNotDebugCapacity(); assert(this != &src); @@ -2665,8 +2698,11 @@ } } - template <typename H, typename E> - void merge(raw_hash_set<Policy, H, E, Alloc>&& src) { + template < + typename... Params2, + typename = std::enable_if_t<std::is_same_v< + Alloc, typename raw_hash_set<Policy, Params2...>::allocator_type>>> + void merge(raw_hash_set<Policy, Params2...>&& src) { // NOLINT merge(src); } @@ -3622,19 +3658,19 @@ }; // Erases all elements that satisfy the predicate `pred` from the container `c`. -template <typename P, typename H, typename E, typename A, typename Predicate> -typename raw_hash_set<P, H, E, A>::size_type EraseIf( - Predicate& pred, raw_hash_set<P, H, E, A>* c) { +template <typename P, typename... Params, typename Predicate> +typename raw_hash_set<P, Params...>::size_type EraseIf( + Predicate& pred, raw_hash_set<P, Params...>* c) { return HashtableFreeFunctionsAccess::EraseIf(pred, c); } // Calls `cb` for all elements in the container `c`. -template <typename P, typename H, typename E, typename A, typename Callback> -void ForEach(Callback& cb, raw_hash_set<P, H, E, A>* c) { +template <typename P, typename... Params, typename Callback> +void ForEach(Callback& cb, raw_hash_set<P, Params...>* c) { return HashtableFreeFunctionsAccess::ForEach(cb, c); } -template <typename P, typename H, typename E, typename A, typename Callback> -void ForEach(Callback& cb, const raw_hash_set<P, H, E, A>* c) { +template <typename P, typename... Params, typename Callback> +void ForEach(Callback& cb, const raw_hash_set<P, Params...>* c) { return HashtableFreeFunctionsAccess::ForEach(cb, c); }
diff --git a/absl/container/internal/raw_hash_set_allocator_test.cc b/absl/container/internal/raw_hash_set_allocator_test.cc index b268d9e..c4bff60 100644 --- a/absl/container/internal/raw_hash_set_allocator_test.cc +++ b/absl/container/internal/raw_hash_set_allocator_test.cc
@@ -142,6 +142,10 @@ using init_type = Tracked<int32_t>; using key_type = int32_t; + using DefaultHash = void; + using DefaultEq = void; + using DefaultAlloc = void; + template <class allocator_type, class... Args> static void construct(allocator_type* alloc, slot_type* slot, Args&&... args) {
diff --git a/absl/container/internal/raw_hash_set_benchmark.cc b/absl/container/internal/raw_hash_set_benchmark.cc index f3e32fd..589f4f5 100644 --- a/absl/container/internal/raw_hash_set_benchmark.cc +++ b/absl/container/internal/raw_hash_set_benchmark.cc
@@ -51,6 +51,10 @@ using key_type = int64_t; using init_type = int64_t; + using DefaultHash = void; + using DefaultEq = void; + using DefaultAlloc = void; + static void construct(void*, int64_t* slot, int64_t v) { *slot = v; } static void destroy(void*, int64_t*) {} static void transfer(void*, int64_t* new_slot, int64_t* old_slot) { @@ -97,6 +101,10 @@ using key_type = std::string; using init_type = std::pair<std::string, std::string>; + using DefaultHash = void; + using DefaultEq = void; + using DefaultAlloc = void; + template <class allocator_type, class... Args> static void construct(allocator_type* alloc, slot_type* slot, Args... args) { std::allocator_traits<allocator_type>::construct(
diff --git a/absl/container/internal/raw_hash_set_probe_benchmark.cc b/absl/container/internal/raw_hash_set_probe_benchmark.cc index 2712160..a09f7d9 100644 --- a/absl/container/internal/raw_hash_set_probe_benchmark.cc +++ b/absl/container/internal/raw_hash_set_probe_benchmark.cc
@@ -58,6 +58,10 @@ using key_type = T; using init_type = T; + using DefaultHash = void; + using DefaultEq = void; + using DefaultAlloc = void; + template <class allocator_type, class Arg> static void construct(allocator_type* alloc, slot_type* slot, const Arg& arg) {
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index e8de41a..0b5ad8f 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc
@@ -382,6 +382,10 @@ using key_type = T; using init_type = T; + using DefaultHash = hash_default_hash<T>; + using DefaultEq = std::equal_to<T>; + using DefaultAlloc = std::allocator<T>; + template <class Allocator, class... Args> static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { absl::allocator_traits<Allocator>::construct(*alloc, slot, @@ -529,6 +533,10 @@ using key_type = std::string; using init_type = std::pair<std::string, std::string>; + using DefaultHash = void; + using DefaultEq = void; + using DefaultAlloc = void; + template <class allocator_type, class... Args> static void construct(allocator_type* alloc, slot_type* slot, Args... args) { std::allocator_traits<allocator_type>::construct( @@ -581,9 +589,9 @@ template <typename T, bool kTransferable = false, bool kSoo = false, class Alloc = std::allocator<T>> -struct ValueTable - : raw_hash_set<ValuePolicy<T, kTransferable, kSoo>, hash_default_hash<T>, - std::equal_to<T>, Alloc> { +struct ValueTable : InstantiateRawHashSet<ValuePolicy<T, kTransferable, kSoo>, + hash_default_hash<T>, + std::equal_to<T>, Alloc>::type { using Base = typename ValueTable::raw_hash_set; using Base::Base; }; @@ -782,6 +790,34 @@ std::equal_to<absl::string_view>, std::allocator<int>>)); } +TEST(InstantiateRawHashSetTest, VerifyTypes) { + using P = ValuePolicy<int>; + using DA = typename P::DefaultHash; + using DB = typename P::DefaultEq; + using DC = typename P::DefaultAlloc; + + struct A {}; + struct B {}; + struct C {}; + + EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, A, B, C>::type, + raw_hash_set<P, A, B, C>>)); + EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, A, B, DC>::type, + raw_hash_set<P, A, B>>)); + EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, A, DB, C>::type, + raw_hash_set<P, A, DB, C>>)); + EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, A, DB, DC>::type, + raw_hash_set<P, A>>)); + EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, DA, B, C>::type, + raw_hash_set<P, DA, B, C>>)); + EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, DA, B, DC>::type, + raw_hash_set<P, DA, B>>)); + EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, DA, DB, C>::type, + raw_hash_set<P, DA, DB, C>>)); + EXPECT_TRUE((std::is_same_v<InstantiateRawHashSet<P, DA, DB, DC>::type, + raw_hash_set<P>>)); +} + template <class TableType> class SooTest : public testing::Test {}; @@ -1144,6 +1180,10 @@ using key_type = DecomposeType; using init_type = DecomposeType; + using DefaultHash = void; + using DefaultEq = void; + using DefaultAlloc = void; + template <typename T> static void construct(void*, DecomposeType* slot, T&& v) { ::new (slot) DecomposeType(std::forward<T>(v)); @@ -2430,8 +2470,9 @@ } }; - struct Table : raw_hash_set<ValuePolicy<Value>, H, std::equal_to<Value>, - std::allocator<Value>> { + struct Table + : InstantiateRawHashSet<ValuePolicy<Value>, H, std::equal_to<Value>, + std::allocator<Value>>::type { using Base = typename Table::raw_hash_set; using Base::Base; };
diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h index b24db36..580a044 100644 --- a/absl/container/node_hash_map.h +++ b/absl/container/node_hash_map.h
@@ -122,13 +122,18 @@ // if (result != ducks.end()) { // std::cout << "Result: " << result->second << std::endl; // } -template <class Key, class Value, class Hash = DefaultHashContainerHash<Key>, - class Eq = DefaultHashContainerEq<Key>, - class Alloc = std::allocator<std::pair<const Key, Value>>> +template < + class Key, class Value, + class Hash = + typename container_internal::NodeHashMapPolicy<Key, Value>::DefaultHash, + class Eq = + typename container_internal::NodeHashMapPolicy<Key, Value>::DefaultEq, + class Alloc = typename container_internal::NodeHashMapPolicy< + Key, Value>::DefaultAlloc> class ABSL_ATTRIBUTE_OWNER node_hash_map - : public absl::container_internal::raw_hash_map< + : public absl::container_internal::InstantiateRawHashMap< absl::container_internal::NodeHashMapPolicy<Key, Value>, Hash, Eq, - Alloc> { + Alloc>::type { using Base = typename node_hash_map::raw_hash_map; public: @@ -629,6 +634,10 @@ using mapped_type = Value; using init_type = std::pair</*non const*/ key_type, mapped_type>; + using DefaultHash = DefaultHashContainerHash<Key>; + using DefaultEq = DefaultHashContainerEq<Key>; + using DefaultAlloc = std::allocator<std::pair<const Key, Value>>; + template <class Allocator, class... Args> static value_type* new_element(Allocator* alloc, Args&&... args) { using PairAlloc = typename absl::allocator_traits<
diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h index 508248c..f69c6ab 100644 --- a/absl/container/node_hash_set.h +++ b/absl/container/node_hash_set.h
@@ -118,11 +118,16 @@ // if (ducks.contains("dewey")) { // std::cout << "We found dewey!" << std::endl; // } -template <class T, class Hash = DefaultHashContainerHash<T>, - class Eq = DefaultHashContainerEq<T>, class Alloc = std::allocator<T>> +template < + class T, + class Hash = typename container_internal::NodeHashSetPolicy<T>::DefaultHash, + class Eq = typename container_internal::NodeHashSetPolicy<T>::DefaultEq, + class Alloc = + typename container_internal::NodeHashSetPolicy<T>::DefaultAlloc> class ABSL_ATTRIBUTE_OWNER node_hash_set - : public absl::container_internal::raw_hash_set< - absl::container_internal::NodeHashSetPolicy<T>, Hash, Eq, Alloc> { + : public absl::container_internal::InstantiateRawHashSet< + absl::container_internal::NodeHashSetPolicy<T>, Hash, Eq, + Alloc>::type { using Base = typename node_hash_set::raw_hash_set; public: @@ -529,6 +534,10 @@ using init_type = T; using constant_iterators = std::true_type; + using DefaultHash = DefaultHashContainerHash<T>; + using DefaultEq = DefaultHashContainerEq<T>; + using DefaultAlloc = std::allocator<T>; + template <class Allocator, class... Args> static T* new_element(Allocator* alloc, Args&&... args) { using ValueAlloc =