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 =