From 6464d0e083a3ee83d4c9dd8738fbb7de30ea431f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Mon, 1 Jan 2024 00:02:45 +0100 Subject: [PATCH] Implement new hashing protocol now that Boost.ContainerHash requires C++11: - Hashes trivially scalar types - For the non scalar types calls unqualified hash_value activating ADL (boost::hash protocol) - If ADL does not find a viable candidate, a boost::hash direct call is performed via a forward declaration, so that additional candidates are looked up if the user has included boost/container_hash/hash.hpp. This maintains a high level of compatibility with boost::hash but making Boost.Intrusive independent from Boost.ContainerHash, so that different C++ standard levels can be used and users using different hash functions don't need direct and indirect dependencies brought by Boost.ContainerHash. --- .gitignore | 5 + doc/intrusive.qbk | 6 +- include/boost/intrusive/detail/hash.hpp | 277 ++++++++++++++++++ .../boost/intrusive/detail/hash_combine.hpp | 60 +--- .../boost/intrusive/detail/hash_integral.hpp | 119 ++++++++ include/boost/intrusive/detail/hash_mix.hpp | 130 ++++++++ include/boost/intrusive/hashtable.hpp | 10 +- .../callable_with_no_decltype.lastbuildstate | 2 + test/hash_functor_test.cpp | 263 +++++++++++++++++ 9 files changed, 820 insertions(+), 52 deletions(-) create mode 100644 .gitignore create mode 100644 include/boost/intrusive/detail/hash.hpp create mode 100644 include/boost/intrusive/detail/hash_integral.hpp create mode 100644 include/boost/intrusive/detail/hash_mix.hpp create mode 100644 proj/vc14.1ide/callable_with_no_decltype/x64/Debug/callable.3579B1A4.tlog/callable_with_no_decltype.lastbuildstate create mode 100644 test/hash_functor_test.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96e4b25 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +################################################################################ +# This .gitignore file was automatically created by Microsoft(R) Visual Studio. +################################################################################ + +/proj/vc14.1ide diff --git a/doc/intrusive.qbk b/doc/intrusive.qbk index 6157d28..b51f977 100644 --- a/doc/intrusive.qbk +++ b/doc/intrusive.qbk @@ -1304,7 +1304,11 @@ And they also can receive additional options: in containers. Default: `equal< std::equal_to >` * [*`hash`]: Hash function to be used in the container. - Default: `hash< boost::hash >` + Default: internal hash functor that hashes scalar types, triggering unqualified call to + `hash_value`for non-scalar types (boost::hash compatible protocol). If + the ADL call does not find a viable candidate a boost::hash() call is tried + in case the user has included boost/container_hash/hash.hpp and a viable + candidate is found there. Fails otherwise. * [*`bucket_traits`]: A type that wraps the bucket vector to be used by the unordered container. Default: a type initialized by the address diff --git a/include/boost/intrusive/detail/hash.hpp b/include/boost/intrusive/detail/hash.hpp new file mode 100644 index 0000000..3ddfa2c --- /dev/null +++ b/include/boost/intrusive/detail/hash.hpp @@ -0,0 +1,277 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Copyright 2005-2014 Daniel James. +// Copyright 2021, 2022 Peter Dimov. +// Copyright 2024 Ion Gaztañaga. +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// Based on Peter Dimov's proposal +// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf +// issue 6.18. +// +// The original C++11 implementation was done by Peter Dimov +// The C++03 porting was done by Ion Gaztanaga. +// +// The goal of this header is to avoid Intrusive's hard dependency on ContainerHash, +// which adds additional dependencies and the minimum supported C++ standard can +// differ between both libraries. However, a compatibility protocol is used so that +// users compatible with ContainerHash are also compatible with Intrusive: +// +// - If users define `hash_value` (as required by boost::hash) for their classes +// are automatically compatible with Intrusive unordered containers. +// +// - If users include boost/container_hash/hash.hpp in their headers, Intrusive +// unordered containers will take advantage of boost::hash compatibility hash functions +// (such as hashing functions for range-compatible types, standard containers, etc.) +// +// See http://www.boost.org/libs/intrusive for documentation. +// +///////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_INTRUSIVE_HASH_HASH_HPP +#define BOOST_INTRUSIVE_HASH_HASH_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { + +template +struct hash; + +} //namespace boost + +//Fallback function to call boost::hash if scalar type and ADL fail. +//The user must include boost/container_hash/hash.hpp when to make this call work, +//this allows boost::intrusive to be compatible with boost::hash without +//a mandatory physical (header inclusion) dependency +namespace boost_intrusive_adl +{ + template + inline std::size_t hash_value(const T& v) + { + return boost::hash()(v); + } +} + +namespace boost { +namespace intrusive { +namespace detail { + +//ADL-based lookup hash call +template +inline typename detail::disable_if_c::value, std::size_t>::type + hash_value_dispatch(const T& v) +{ + //Try ADL lookup, if it fails, boost_intrusive_adl::hash_value will retry with boost::hash + using boost_intrusive_adl::hash_value; + return hash_value(v); +} + +template +typename enable_if_c::value, std::size_t>::type + hash_value( T v ) +{ + return static_cast( v ); +} + +//////////////////////////////////////////////////////////// +// +// floating point types +// +//////////////////////////////////////////////////////////// + +template +struct hash_float_impl; + +// float +template struct hash_float_impl +{ + static std::size_t fn( T v ) + { + boost::uint32_t w; + std::memcpy( &w, &v, sizeof( v ) ); + + return w; + } +}; + +// double +template struct hash_float_impl +{ + static std::size_t fn( T v ) + { + boost::uint64_t w; + std::memcpy( &w, &v, sizeof( v ) ); + + return hash_value( w ); + } +}; + +// 80 bit long double in 12 bytes +template struct hash_float_impl +{ + static std::size_t fn( T v ) + { + boost::uint64_t w[ 2 ] = {}; + std::memcpy( &w, &v, 80 / CHAR_BIT ); + + std::size_t seed = 0; + + seed = hash_value( w[0] ) + (hash_mix)( seed ); + seed = hash_value( w[1] ) + (hash_mix)( seed ); + + return seed; + } +}; + +#if (LDBL_MAX_10_EXP == 4932) + +// 80 bit long double in 16 bytes +template struct hash_float_impl +{ + static std::size_t fn( T v ) + { + boost::uint64_t w[ 2 ] = {}; + std::memcpy( &w, &v, 80 / CHAR_BIT ); + + std::size_t seed = 0; + + seed = hash_value( w[0] ) + (hash_mix)( seed ); + seed = hash_value( w[1] ) + (hash_mix)( seed ); + + return seed; + } +}; + +#elif (LDBL_MAX_10_EXP > 4932) +// 128 bit long double +template struct hash_float_impl +{ + static std::size_t fn( T v ) + { + boost::uint64_t w[ 2 ]; + std::memcpy( &w, &v, sizeof( v ) ); + + std::size_t seed = 0; + + #if defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__ + + seed = hash_value( w[1] ) + (hash_mix)( seed ); + seed = hash_value( w[0] ) + (hash_mix)( seed ); + + #else + + seed = hash_value( w[0] ) + (hash_mix)( seed ); + seed = hash_value( w[1] ) + (hash_mix)( seed ); + + #endif + return seed; + } +}; +#endif //#if (LDBL_MAX_10_EXP == 4932) + +template +typename enable_if_c::value, std::size_t>::type + hash_value( T v ) +{ + return boost::intrusive::detail::hash_float_impl::fn( v + 0 ); +} + +//////////////////////////////////////////////////////////// +// +// pointer types +// +//////////////////////////////////////////////////////////// +// `x + (x >> 3)` adjustment by Alberto Barbati and Dave Harris. +template std::size_t hash_value( T* const& v ) +{ + std::size_t x = reinterpret_cast( v ); + return hash_value( x + (x >> 3) ); +} + +//////////////////////////////////////////////////////////// +// +// std::nullptr_t +// +//////////////////////////////////////////////////////////// +#if !defined(BOOST_NO_CXX11_NULLPTR) +template +typename enable_if_c::value, std::size_t>::type + hash_value( T const &) +{ + return (hash_value)( static_cast( nullptr ) ); +} + #endif + +//////////////////////////////////////////////////////////// +// +// Array types +// +//////////////////////////////////////////////////////////// + +//Forward declaration or internal hash functor, for array iteration +template +struct internal_hash_functor; + +template +inline std::size_t hash_value_dispatch( T const (&x)[ N ] ) +{ + std::size_t seed = 0; + for(std::size_t i = 0; i != N; ++i){ + hash_combine_size_t(seed, internal_hash_functor()(x[i])); + } + return seed; +} + +template +inline std::size_t hash_value_dispatch( T (&x)[ N ] ) +{ + std::size_t seed = 0; + for (std::size_t i = 0; i != N; ++i) { + hash_combine_size_t(seed, internal_hash_functor()(x[i])); + } + return seed; +} + +//////////////////////////////////////////////////////////// +// +// Scalar types, calls proper overload +// +//////////////////////////////////////////////////////////// +template +inline typename detail::enable_if_c::value, std::size_t>::type + hash_value_dispatch(const T &v) +{ + return boost::intrusive::detail::hash_value(v); +} + +//Internal "anonymous" hash functor, first selects between "built-in" scalar/array types +//and ADL-based lookup + +template +struct internal_hash_functor +{ + inline std::size_t operator()(T const& val) const + { + return ::boost::intrusive::detail::hash_value_dispatch(val); + } +}; + +} // namespace detail { +} // namespace intrusive { +} // namespace boost + +#include + +#endif // #ifndef BOOST_INTRUSIVE_HASH_HASH_HPP + diff --git a/include/boost/intrusive/detail/hash_combine.hpp b/include/boost/intrusive/detail/hash_combine.hpp index f089fb0..aa6f797 100644 --- a/include/boost/intrusive/detail/hash_combine.hpp +++ b/include/boost/intrusive/detail/hash_combine.hpp @@ -1,3 +1,5 @@ +///////////////////////////////////////////////////////////////////////////// +// // Copyright 2005-2014 Daniel James. // 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) @@ -13,9 +15,14 @@ // domain. The author hereby disclaims copyright to this source code. // // Copyright 2021 Ion Gaztanaga -// Refactored the original boost/container_hash/hash.hpp to avoid +// Refactored the original Boost ContainerHash library to avoid // any heavy std header dependencies to just combine two hash // values represented in a std::size_t type. +// +// See http://www.boost.org/libs/intrusive for documentation. +// +///////////////////////////////////////////////////////////////////////////// + #ifndef BOOST_INTRUSIVE_DETAIL_HASH_COMBINE_HPP #define BOOST_INTRUSIVE_DETAIL_HASH_COMBINE_HPP @@ -29,62 +36,17 @@ #endif #include - -#if defined(_MSC_VER) -# include -# define BOOST_INTRUSIVE_HASH_ROTL32(x, r) _rotl(x,r) -#else -# define BOOST_INTRUSIVE_HASH_ROTL32(x, r) (x << r) | (x >> (32 - r)) -#endif +#include "hash_mix.hpp" namespace boost { namespace intrusive { namespace detail { -template -inline void hash_combine_size_t(SizeT& seed, SizeT value) +inline void hash_combine_size_t(std::size_t& seed, std::size_t value) { - seed ^= value + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed = boost::intrusive::detail::hash_mix(seed + 0x9e3779b9 + value); } -inline void hash_combine_size_t(boost::uint32_t& h1, boost::uint32_t k1) -{ - const uint32_t c1 = 0xcc9e2d51; - const uint32_t c2 = 0x1b873593; - - k1 *= c1; - k1 = BOOST_INTRUSIVE_HASH_ROTL32(k1,15); - k1 *= c2; - - h1 ^= k1; - h1 = BOOST_INTRUSIVE_HASH_ROTL32(h1,13); - h1 = h1*5+0xe6546b64; -} - - - // Don't define 64-bit hash combine on platforms without 64 bit integers, - // and also not for 32-bit gcc as it warns about the 64-bit constant. - #if !defined(BOOST_NO_INT64_T) && \ - !(defined(__GNUC__) && ULONG_MAX == 0xffffffff) - inline void hash_combine_size_t(boost::uint64_t& h, boost::uint64_t k) - { - const boost::uint64_t m = UINT64_C(0xc6a4a7935bd1e995); - const int r = 47; - - k *= m; - k ^= k >> r; - k *= m; - - h ^= k; - h *= m; - - // Completely arbitrary number, to prevent 0's - // from hashing to 0. - h += 0xe6546b64; - } - - #endif // BOOST_NO_INT64_T - } //namespace detail { } //namespace intrusive { } //namespace boost { diff --git a/include/boost/intrusive/detail/hash_integral.hpp b/include/boost/intrusive/detail/hash_integral.hpp new file mode 100644 index 0000000..37ab809 --- /dev/null +++ b/include/boost/intrusive/detail/hash_integral.hpp @@ -0,0 +1,119 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Copyright 2021-2023 Peter Dimov +// Copyright 2024 Ion Gaztanaga +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// The original C++11 implementation was done by Peter Dimov +// The C++03 porting was done by Ion Gaztanaga +// +// See http://www.boost.org/libs/intrusive for documentation. +// +///////////////////////////////////////////////////////////////////////////// + + +#ifndef BOOST_INTRUSIVE_DETAIL_HASH_INTEGRAL_HPP +#define BOOST_INTRUSIVE_DETAIL_HASH_INTEGRAL_HPP + +#include +#include "hash_mix.hpp" +#include +#include +#include + +namespace boost { +namespace intrusive { +namespace detail { + + +template sizeof(std::size_t)), + bool is_unsigned = is_unsigned::value, + std::size_t size_t_bits = sizeof(std::size_t) * CHAR_BIT, + std::size_t type_bits = sizeof(T) * CHAR_BIT> +struct hash_integral_impl; + +template +struct hash_integral_impl +{ + static std::size_t fn( T v ) + { + return static_cast( v ); + } +}; + +template +struct hash_integral_impl +{ + static std::size_t fn( T v ) + { + typedef typename make_unsigned::type U; + + if( v >= 0 ) + { + return hash_integral_impl::fn( static_cast( v ) ); + } + else + { + return ~hash_integral_impl::fn( static_cast( ~static_cast( v ) ) ); + } + } +}; + +template +struct hash_integral_impl +{ + static std::size_t fn( T v ) + { + std::size_t seed = 0; + + seed = static_cast( v >> 32 ) + (hash_mix)( seed ); + seed = static_cast( v & 0xFFFFFFFF ) + (hash_mix)( seed ); + + return seed; + } +}; + +template +struct hash_integral_impl +{ + static std::size_t fn( T v ) + { + std::size_t seed = 0; + + seed = static_cast( v >> 96 ) + (hash_mix)( seed ); + seed = static_cast( v >> 64 ) + (hash_mix)( seed ); + seed = static_cast( v >> 32 ) + (hash_mix)( seed ); + seed = static_cast( v ) + (hash_mix)( seed ); + + return seed; + } +}; + +template +struct hash_integral_impl +{ + static std::size_t fn( T v ) + { + std::size_t seed = 0; + + seed = static_cast( v >> 64 ) + (hash_mix)( seed ); + seed = static_cast( v ) + (hash_mix)( seed ); + + return seed; + } +}; + +template +typename enable_if_c::value, std::size_t>::type + hash_value( T v ) +{ + return hash_integral_impl::fn( v ); +} + +} // namespace detail +} // namespace intrusive +} // namespace boost + +#endif // #ifndef BOOST_INTRUSIVE_DETAIL_HASH_INTEGRAL_HPP diff --git a/include/boost/intrusive/detail/hash_mix.hpp b/include/boost/intrusive/detail/hash_mix.hpp new file mode 100644 index 0000000..a78b169 --- /dev/null +++ b/include/boost/intrusive/detail/hash_mix.hpp @@ -0,0 +1,130 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Copyright 2022 Peter Dimov +// Copyright 2024 Ion Gaztanaga +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// The original C++11 implementation was done by Peter Dimov +// The C++03 porting was done by Ion Gaztanaga +// +// Refactored the original Boost ContainerHash library to avoid +// any heavy std header dependencies to just mix a hash +// value represented in a std::size_t type. +// +// See http://www.boost.org/libs/intrusive for documentation. +// +///////////////////////////////////////////////////////////////////////////// + + +#ifndef BOOST_INTRUSIVE_DETAIL_HASH_MIX_HPP +#define BOOST_INTRUSIVE_DETAIL_HASH_MIX_HPP + +#include //boost::uint64_t +#include +#include + +namespace boost { +namespace intrusive { +namespace detail { + + +template struct hash_mix_impl; + +// hash_mix for 64 bit size_t +// +// The general "xmxmx" form of state of the art 64 bit mixers originates +// from Murmur3 by Austin Appleby, which uses the following function as +// its "final mix": +// +// k ^= k >> 33; +// k *= 0xff51afd7ed558ccd; +// k ^= k >> 33; +// k *= 0xc4ceb9fe1a85ec53; +// k ^= k >> 33; +// +// (https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp) +// +// It has subsequently been improved multiple times by different authors +// by changing the constants. The most well known improvement is the +// so-called "variant 13" function by David Stafford: +// +// k ^= k >> 30; +// k *= 0xbf58476d1ce4e5b9; +// k ^= k >> 27; +// k *= 0x94d049bb133111eb; +// k ^= k >> 31; +// +// (https://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html) +// +// This mixing function is used in the splitmix64 RNG: +// http://xorshift.di.unimi.it/splitmix64.c +// +// We use Jon Maiga's implementation from +// http://jonkagstrom.com/mx3/mx3_rev2.html +// +// x ^= x >> 32; +// x *= 0xe9846af9b1a615d; +// x ^= x >> 32; +// x *= 0xe9846af9b1a615d; +// x ^= x >> 28; +// +// An equally good alternative is Pelle Evensen's Moremur: +// +// x ^= x >> 27; +// x *= 0x3C79AC492BA7B653; +// x ^= x >> 33; +// x *= 0x1C69B3F74AC4AE35; +// x ^= x >> 27; +// +// (https://mostlymangling.blogspot.com/2019/12/stronger-better-morer-moremur-better.html) + +template<> struct hash_mix_impl<64> +{ + inline static boost::uint64_t fn( boost::uint64_t x ) + { + boost::uint64_t const m = 0xe9846af9b1a615d; + + x ^= x >> 32; + x *= m; + x ^= x >> 32; + x *= m; + x ^= x >> 28; + + return x; + } +}; + +// hash_mix for 32 bit size_t +// +// We use the "best xmxmx" implementation from +// https://github.com/skeeto/hash-prospector/issues/19 + +template<> struct hash_mix_impl<32> +{ + inline static boost::uint32_t fn( boost::uint32_t x ) + { + boost::uint32_t const m1 = 0x21f0aaad; + boost::uint32_t const m2 = 0x735a2d97; + + x ^= x >> 16; + x *= m1; + x ^= x >> 15; + x *= m2; + x ^= x >> 15; + + return x; + } +}; + +inline std::size_t hash_mix( std::size_t v ) +{ + return hash_mix_impl::fn( v ); +} + + +} // namespace detail +} // namespace intrusive +} // namespace boost + +#endif // #ifndef BOOST_INTRUSIVE_DETAIL_HASH_MIX_HPP diff --git a/include/boost/intrusive/hashtable.hpp b/include/boost/intrusive/hashtable.hpp index 11c81d7..8a0e01d 100644 --- a/include/boost/intrusive/hashtable.hpp +++ b/include/boost/intrusive/hashtable.hpp @@ -32,6 +32,8 @@ #include #include +#include + //General intrusive utilities #include #include @@ -57,7 +59,6 @@ #include //boost -#include #include #include #include @@ -69,6 +70,9 @@ #include //std::size_t #include //std::uint64_t + +#include "detail/hash.hpp" + #if defined(BOOST_HAS_PRAGMA_ONCE) # pragma once #endif @@ -79,8 +83,10 @@ namespace boost { + namespace intrusive { + #if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) /// @cond @@ -1394,7 +1400,7 @@ struct get_hash template struct get_hash { - typedef ::boost::hash type; + typedef detail::internal_hash_functor type; }; template diff --git a/proj/vc14.1ide/callable_with_no_decltype/x64/Debug/callable.3579B1A4.tlog/callable_with_no_decltype.lastbuildstate b/proj/vc14.1ide/callable_with_no_decltype/x64/Debug/callable.3579B1A4.tlog/callable_with_no_decltype.lastbuildstate new file mode 100644 index 0000000..20b5ebe --- /dev/null +++ b/proj/vc14.1ide/callable_with_no_decltype/x64/Debug/callable.3579B1A4.tlog/callable_with_no_decltype.lastbuildstate @@ -0,0 +1,2 @@ +PlatformToolSet=v143:VCToolArchitecture=Native64Bit:VCToolsVersion=14.35.32215:TargetPlatformVersion=10.0.19041.0: +Debug|x64|C:\Data\Libs\LocalGit\boost\libs\intrusive\proj\vc14.1ide\| diff --git a/test/hash_functor_test.cpp b/test/hash_functor_test.cpp new file mode 100644 index 0000000..5a75093 --- /dev/null +++ b/test/hash_functor_test.cpp @@ -0,0 +1,263 @@ +///////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2023-2024. +// +// 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) +// +// See http://www.boost.org/libs/intrusive for documentation. +// +///////////////////////////////////////////////////////////////////////////// +#include + +#ifdef NDEBUG +#undef NDEBUG +#endif +#include +#include + +using namespace boost::intrusive; + + +//Generic class that stores a key as "first" element +template +struct first_key; + +template +class FirstKeyClass : public unordered_set_base_hook<> +{ +public: + Key first; + + explicit FirstKeyClass() : first() {} +}; + +template +struct equal_type + : boost::intrusive::value_equal +{}; + +template +struct equal_type +{ + bool operator() (T const (&a)[N], T const (&b)[N]) + { + for (std::size_t i = 0; i != N; ++i) { + if (a[i] != b[i]) + return false; + } + + return true; + } +}; + +//key_of_value argument when isnerting FirstKeyClass objects in unordered map +template +struct first_key +{ + typedef Key type; + + const type& operator()(const FirstKeyClass& v) const + { + return v.first; + } +}; + +//Function that instantiates an unordered intrusive container that stores FirstKeyClass objects +template +void instantiate_first_key_unordered() +{ + typedef unordered_set< FirstKeyClass, key_of_value >, equal > > UnorderedMap; + typedef typename UnorderedMap::bucket_type bucket_type; + typedef typename UnorderedMap::bucket_traits bucket_traits; + typedef typename UnorderedMap::value_type value_type; + typedef typename UnorderedMap::key_of_value key_of_value; + + const std::size_t bucket_len = 2; + bucket_type buckets[bucket_len]; + + UnorderedMap u(bucket_traits(buckets, bucket_len)); + value_type v; + assert(u.find(key_of_value()(v)) == u.end()); + u.insert(v); + value_type v2; + assert(u.find(v2.first) != u.end()); + u.clear(); +} + +//Function that instantiates typical values in an unordered intrusive container +template +void instantiate_value_unordered() +{ + typedef unordered_set< ValueType > Unordered; + typedef typename Unordered::bucket_type bucket_type; + typedef typename Unordered::bucket_traits bucket_traits; + typedef typename Unordered::value_type value_type; + + const std::size_t bucket_len = 2; + bucket_type buckets[bucket_len]; + + Unordered u(bucket_traits(buckets, bucket_len)); + value_type v; + assert(u.find(v) == u.end()); + u.insert(v); + value_type v2; + assert(u.find(v2) != u.end()); + u.clear(); +} + +enum MyEnum +{ + MyZero, + MyOne, + MyTwo +}; + +//Function that tests all scalar types +void test_first_key_scalar_unordered() +{ + //characters + instantiate_first_key_unordered(); + +#ifndef BOOST_NO_INTRINSIC_WCHAR_T + instantiate_first_key_unordered(); +#endif +#ifndef BOOST_NO_CXX11_CHAR16_T + instantiate_first_key_unordered(); +#endif +#ifndef BOOST_NO_CXX11_CHAR32_T + instantiate_first_key_unordered(); +#endif +#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L + instantiate_first_key_unordered(); +#endif + + //integers + instantiate_first_key_unordered(); + instantiate_first_key_unordered(); + instantiate_first_key_unordered(); + instantiate_first_key_unordered(); + instantiate_first_key_unordered(); + instantiate_first_key_unordered(); + instantiate_first_key_unordered(); + instantiate_first_key_unordered(); +#ifdef BOOST_HAS_LONG_LONG + instantiate_first_key_unordered< ::boost::long_long_type>(); + instantiate_first_key_unordered< ::boost::ulong_long_type>(); +#endif +#ifdef BOOST_HAS_INT128 + instantiate_first_key_unordered< ::boost::int128_type>(); + instantiate_first_key_unordered< ::boost::uint128_type>(); +#endif + + //floating + instantiate_first_key_unordered(); + instantiate_first_key_unordered(); + instantiate_first_key_unordered(); + + //Array + instantiate_first_key_unordered(); + instantiate_first_key_unordered(); + + //Pointer + instantiate_first_key_unordered(); + instantiate_first_key_unordered(); + + //std::nullptr_t +#if !defined(BOOST_NO_CXX11_NULLPTR) + instantiate_first_key_unordered(); +#endif + + //Enum + instantiate_first_key_unordered(); +} + +namespace some_ns { + + template + class HashValueDefined : public unordered_set_base_hook<> + { + private: + T val; + + public: + HashValueDefined() : val() {} + + friend bool operator==(const HashValueDefined& a, const HashValueDefined& b) + { + return a.val == b.val; + } + + friend std::size_t hash_value(const HashValueDefined& a) + { + return static_cast(a.val); + } + }; + +} // namespace some_ns { + +template +class HashValueDefined2 : public unordered_set_base_hook<> +{ +private: + T val; + +public: + HashValueDefined2() : val() {} + + friend bool operator==(const HashValueDefined2& a, const HashValueDefined2& b) + { + return a.val == b.val; + } + + friend std::size_t hash_value(const HashValueDefined2& a) + { + return static_cast(a.val); + } +}; + +void test_value_unordered() +{ + instantiate_value_unordered >(); + instantiate_value_unordered >(); + instantiate_value_unordered >(); + instantiate_value_unordered >(); + instantiate_value_unordered >(); + instantiate_value_unordered >(); +} + + +#if (BOOST_CXX_VERSION >= 201103L) +#include +#include +#include + +template +class DerivedFromType : public unordered_set_base_hook<>, public StdBase +{ + friend bool operator==(const DerivedFromType& a, const DerivedFromType& b) + { + return static_cast(a) == static_cast(b); + } +}; + +void test_value_stdbase_unordered() +{ + instantiate_first_key_unordered >(); + instantiate_first_key_unordered > >(); + instantiate_value_unordered >(); + instantiate_value_unordered > >(); +} +#endif + + +int main() +{ + test_first_key_scalar_unordered(); + test_value_unordered(); +#if (BOOST_CXX_VERSION >= 201103L) + test_value_stdbase_unordered(); +#endif + return 0; +}