From fece11142c2976c16ec481b07efec3374a396b06 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sat, 11 Jan 2020 04:51:30 +0200 Subject: [PATCH] Add hashing support --- include/boost/variant2/variant.hpp | 79 ++++++++++++++++++++++++++++++ test/Jamfile | 2 + test/variant_hash.cpp | 73 +++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 test/variant_hash.cpp diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index d661ca5..df78adf 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -18,6 +18,7 @@ #endif #include #include +#include #include #include #include @@ -1785,9 +1786,87 @@ void swap( variant & v, variant & w ) v.swap( w ); } +// hashing support + +namespace detail +{ + +template struct hash_value_L +{ + V const & v; + + template std::size_t operator()( I ) const + { + boost::ulong_long_type hv = ( boost::ulong_long_type( 0xCBF29CE4 ) << 32 ) + 0x84222325; + boost::ulong_long_type const prime = ( boost::ulong_long_type( 0x00000100 ) << 32 ) + 0x000001B3; + + // index + + hv ^= I::value; + hv *= prime; + + // value + + auto const & t = unsafe_get( v ); + + hv ^= std::hash>()( t ); + hv *= prime; + + return static_cast( hv ); + } +}; + +} // namespace detail + +std::size_t hash_value( monostate const & ) +{ + return 0xA7EE4757u; +} + +template std::size_t hash_value( variant const & v ) +{ + return mp11::mp_with_index( v.index(), detail::hash_value_L< variant >{ v } ); +} + +namespace detail +{ + +template using is_hash_enabled = std::is_default_constructible< std::hash::type> >; + +template::value> struct std_hash_impl; + +template struct std_hash_impl +{ + std_hash_impl() = delete; + std_hash_impl( std_hash_impl const& ) = delete; + std_hash_impl& operator=( std_hash_impl const& ) = delete; +}; + +template struct std_hash_impl +{ + std::size_t operator()( V const & v ) const + { + return hash_value( v ); + } +}; + +} // namespace detail + } // namespace variant2 } // namespace boost +template struct std::hash< boost::variant2::variant >: public boost::variant2::detail::std_hash_impl< boost::variant2::variant > +{ +}; + +template<> struct std::hash< boost::variant2::monostate > +{ + std::size_t operator()( boost::variant2::monostate const & v ) const + { + return hash_value( v ); + } +}; + #if defined(_MSC_VER) && _MSC_VER < 1910 # pragma warning( pop ) #endif diff --git a/test/Jamfile b/test/Jamfile index 22cfe2d..73b2498 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -105,3 +105,5 @@ run variant_get_by_type.cpp throw_exception.cpp : : : $(NX) : variant_get_by_typ compile variant_get_by_type_cx.cpp : $(NX) : variant_get_by_type_cx_nx ; run variant_subset.cpp throw_exception.cpp : : : $(NX) : variant_subset_nx ; + +run variant_hash.cpp ; diff --git a/test/variant_hash.cpp b/test/variant_hash.cpp new file mode 100644 index 0000000..bcc264b --- /dev/null +++ b/test/variant_hash.cpp @@ -0,0 +1,73 @@ + +// Copyright 2020 Peter Dimov. +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#if defined(_MSC_VER) +# pragma warning( disable: 4244 ) // conversion from int to float, possible loss of data +#endif + +#include +#include +#include +#include +#include + +using namespace boost::variant2; + +template class Hash, class T1, class T2, class T3> void test() +{ + variant v1( in_place_index_t<0>{} ); + std::size_t h1 = Hash()( v1 ); + + variant v2( in_place_index_t<1>{} ); + std::size_t h2 = Hash()( v2 ); + + variant v3( in_place_index_t<2>{} ); + std::size_t h3 = Hash()( v3 ); + + BOOST_TEST_NE( h1, h2 ); + BOOST_TEST_NE( h1, h3 ); + BOOST_TEST_NE( h2, h3 ); +} + +template class Hash, class T> void test2() +{ + variant v1( 0 ); + std::size_t h1 = Hash()( v1 ); + + variant v2( 1 ); + std::size_t h2 = Hash()( v2 ); + + variant v3( 2 ); + std::size_t h3 = Hash()( v3 ); + + BOOST_TEST_NE( h1, h2 ); + BOOST_TEST_NE( h1, h3 ); + BOOST_TEST_NE( h2, h3 ); +} + +struct X {}; + +int main() +{ + test(); + test(); + + test(); + test(); + + test2(); + test2(); + + test2(); + test2(); + +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1910) + + BOOST_TEST_TRAIT_FALSE(( detail::is_hash_enabled )); + +#endif + + return boost::report_errors(); +}