diff --git a/benchmark/string.cpp b/benchmark/string.cpp index 5f4cbc13..dd190f6e 100644 --- a/benchmark/string.cpp +++ b/benchmark/string.cpp @@ -1,5 +1,5 @@ // Copyright 2021 Peter Dimov. -// Copyright 2023 Joaquin M Lopez Munoz. +// Copyright 2023-2024 Joaquin M Lopez Munoz. // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -25,6 +25,7 @@ #include #include #include +#include using namespace std::chrono_literals; @@ -341,7 +342,7 @@ template<> struct fnv1a_hash_impl<64> struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits::digits > { - using is_avalanching = void; + using is_avalanching = std::true_type; }; template using std_unordered_map_fnv1a = diff --git a/benchmark/string_stats.cpp b/benchmark/string_stats.cpp index b3bd3ba6..513681d5 100644 --- a/benchmark/string_stats.cpp +++ b/benchmark/string_stats.cpp @@ -18,6 +18,7 @@ #include #include #include +#include using namespace std::chrono_literals; @@ -262,7 +263,7 @@ template<> struct fnv1a_hash_impl<64> struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits::digits > { - using is_avalanching = void; + using is_avalanching = std::true_type; }; template using boost_unordered_flat_map_fnv1a = @@ -272,7 +273,7 @@ template using boost_unordered_flat_map_fnv1a = struct slightly_bad_hash { - using is_avalanching = void; + using is_avalanching = std::true_type; std::size_t operator()( std::string const& s ) const { @@ -295,7 +296,7 @@ template using boost_unordered_flat_map_slightly_bad_hash = struct bad_hash { - using is_avalanching = void; + using is_avalanching = std::true_type; std::size_t operator()( std::string const& s ) const { diff --git a/benchmark/string_view.cpp b/benchmark/string_view.cpp index 2b1583be..df37cd12 100644 --- a/benchmark/string_view.cpp +++ b/benchmark/string_view.cpp @@ -1,5 +1,5 @@ // Copyright 2021 Peter Dimov. -// Copyright 2023 Joaquin M Lopez Munoz. +// Copyright 2023-2024 Joaquin M Lopez Munoz. // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -26,6 +26,7 @@ #include #include #include +#include using namespace std::chrono_literals; @@ -342,7 +343,7 @@ template<> struct fnv1a_hash_impl<64> struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits::digits > { - using is_avalanching = void; + using is_avalanching = std::true_type; }; template using std_unordered_map_fnv1a = diff --git a/doc/unordered/changes.adoc b/doc/unordered/changes.adoc index b59bb90b..56a2dd4c 100644 --- a/doc/unordered/changes.adoc +++ b/doc/unordered/changes.adoc @@ -10,6 +10,7 @@ * Added container `pmr` aliases when header `` is available. The alias `boost::unordered::pmr::[container]` refers to `boost::unordered::[container]` with a `std::pmr::polymorphic_allocator` allocator type. * Equipped open-addressing and concurrent containers to internally calculate and provide statistical metrics affected by the quality of the hash function. This functionality is enabled by the global macro `BOOST_UNORDERED_ENABLE_STATS`. +* Avalanching hash functions must now be marked via an `is_avalanching` typedef with an embedded `value` constant set to `true` (typically, defining `is_avalanching` as `std::true_type`). `using is_avalanching = void` is deprecated but allowed for backwards compatibility. == Release 1.85.0 diff --git a/doc/unordered/hash_quality.adoc b/doc/unordered/hash_quality.adoc index f8c94871..828e9421 100644 --- a/doc/unordered/hash_quality.adoc +++ b/doc/unordered/hash_quality.adoc @@ -33,7 +33,7 @@ follows: ---- struct my_string_hash_function { - using is_avalanching = void; // instruct Boost.Unordered to not use post-mixing + using is_avalanching = std::true_type; // instruct Boost.Unordered to not use post-mixing std::size_t operator()(const std::string& x) const { diff --git a/doc/unordered/hash_traits.adoc b/doc/unordered/hash_traits.adoc index ae2ca67e..0abd5dcf 100644 --- a/doc/unordered/hash_traits.adoc +++ b/doc/unordered/hash_traits.adoc @@ -32,9 +32,14 @@ large changes in the returned hash code —ideally, flipping one bit in the the input value results in each bit of the hash code flipping with probability 50%. Approaching this property is critical for the proper behavior of open-addressing hash containers. -`hash_is_avalanching::value` is `true` if `Hash::is_avalanching` is a valid type, -and `false` otherwise. -Users can then declare a hash function `Hash` as avalanching either by embedding an `is_avalanching` typedef +`hash_is_avalanching::value` is: + + * `false` if `Hash::is_avalanching` is not present, + * `Hash::is_avalanching::value` if this is present and convertible at compile time to a `bool`, + * `true` if `Hash::is_avalanching` is `void` (this usage is deprecated). + +The behavior is undefined if none of the three cases above is met. +Users can then declare a hash function `Hash` as avalanching either by embedding an appropriate `is_avalanching` typedef into the definition of `Hash`, or directly by specializing `hash_is_avalanching` to a class with an embedded compile-time constant `value` set to `true`. diff --git a/include/boost/unordered/hash_traits.hpp b/include/boost/unordered/hash_traits.hpp index d5f94a78..24fdf808 100644 --- a/include/boost/unordered/hash_traits.hpp +++ b/include/boost/unordered/hash_traits.hpp @@ -1,6 +1,6 @@ /* Hash function characterization. * - * Copyright 2022 Joaquin M Lopez Munoz. + * Copyright 2022-2024 Joaquin M Lopez Munoz. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -19,12 +19,28 @@ namespace unordered{ namespace detail{ template -struct hash_is_avalanching_impl: std::false_type{}; +struct hash_is_avalanching_impl:std::false_type{}; + +template +struct avalanching_value +{ + static constexpr bool value=IsAvalanching::value; +}; + +/* may be explicitly marked as BOOST_DEPRECATED in the future */ +template<> struct avalanching_value +{ + static constexpr bool value=true; +}; template -struct hash_is_avalanching_impl >: - std::true_type{}; +struct hash_is_avalanching_impl< + Hash, + boost::unordered::detail::void_t +>:std::integral_constant< + bool, + avalanching_value::value +>{}; } /* namespace detail */ @@ -32,8 +48,12 @@ struct hash_is_avalanching_impl::value is true when the type Hash::is_avalanching - * is present, false otherwise. +/* hash_is_avalanching::value is: + * - false if Hash::is_avalanching is not present. + * - Hash::is_avalanching::value if this is present and constexpr-convertible + * to a bool. + * - true if Hash::is_avalanching is void (deprecated). + * UB otherwise. */ template struct hash_is_avalanching: detail::hash_is_avalanching_impl::type{}; diff --git a/test/unordered/hash_is_avalanching_test.cpp b/test/unordered/hash_is_avalanching_test.cpp index 1e25e033..7ba7e083 100644 --- a/test/unordered/hash_is_avalanching_test.cpp +++ b/test/unordered/hash_is_avalanching_test.cpp @@ -1,4 +1,5 @@ // Copyright 2022 Peter Dimov +// Copyright 2024 Joaquin M Lopez Munoz // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -26,6 +27,7 @@ namespace unordered #include #include +#include struct X1 { @@ -36,6 +38,27 @@ struct X2 typedef void is_avalanching; }; +struct X4 +{ + using is_avalanching = std::false_type; +}; + +struct X5 +{ + using is_avalanching = std::true_type; +}; + +struct X6 +{ + using is_avalanching = boost::false_type; +}; + +struct X7 +{ + using is_avalanching = boost::true_type; +}; + + int main() { using boost::unordered::hash_is_avalanching; @@ -43,6 +66,10 @@ int main() BOOST_TEST_TRAIT_FALSE((hash_is_avalanching)); BOOST_TEST_TRAIT_TRUE((hash_is_avalanching)); BOOST_TEST_TRAIT_TRUE((hash_is_avalanching)); + BOOST_TEST_TRAIT_FALSE((hash_is_avalanching)); + BOOST_TEST_TRAIT_TRUE((hash_is_avalanching)); + BOOST_TEST_TRAIT_FALSE((hash_is_avalanching)); + BOOST_TEST_TRAIT_TRUE((hash_is_avalanching)); return boost::report_errors(); }