diff --git a/include/boost/container_hash/hash.hpp b/include/boost/container_hash/hash.hpp index c34a78c..a00132b 100644 --- a/include/boost/container_hash/hash.hpp +++ b/include/boost/container_hash/hash.hpp @@ -26,7 +26,15 @@ #include #include #include +#include +#include +#include #include + +#if defined(BOOST_DESCRIBE_CXX14) +# include +#endif + #include #include #include @@ -384,6 +392,49 @@ namespace boost return boost::hash_unordered_range( v.begin(), v.end() ); } +#endif + + // described classes + +#if defined(BOOST_DESCRIBE_CXX14) + +#if defined(_MSC_VER) && _MSC_VER == 1900 +# pragma warning(push) +# pragma warning(disable: 4100) // unreferenced formal parameter +#endif + + template + typename boost::enable_if_::value, std::size_t>::type + hash_value( T const& v ) + { + static_assert( !boost::is_union::value, "described unions are not supported" ); + + std::size_t r = 0; + + using Bd = describe::describe_bases; + + mp11::mp_for_each([&](auto D){ + + using B = typename decltype(D)::type; + boost::hash_combine( r, (B const&)v ); + + }); + + using Md = describe::describe_members; + + mp11::mp_for_each([&](auto D){ + + boost::hash_combine( r, v.*D.pointer ); + + }); + + return r; + } + +#if defined(_MSC_VER) && _MSC_VER == 1900 +# pragma warning(pop) +#endif + #endif // std::unique_ptr, std::shared_ptr diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 2400270..f0a2cf1 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -109,3 +109,6 @@ run is_described_class_test2.cpp : : : extra ; run is_described_class_test3.cpp : : : extra ; + +run described_class_test.cpp + : : : extra ; diff --git a/test/described_class_test.cpp b/test/described_class_test.cpp new file mode 100644 index 0000000..b0d29d7 --- /dev/null +++ b/test/described_class_test.cpp @@ -0,0 +1,155 @@ +// Copyright 2022 Peter Dimov. +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +#if !defined(BOOST_DESCRIBE_CXX14) + +#include + +BOOST_PRAGMA_MESSAGE( "Skipping test because BOOST_DESCRIBE_CXX14 is not defined" ) + +int main() {} + +#else + +struct X1 +{ + int m; + explicit X1( int m_ ): m( m_ ) {} +}; + +BOOST_DESCRIBE_STRUCT( X1, (), (m) ) + +struct X2 +{ + int m; + explicit X2( int m_ ): m( m_ ) {} +}; + +BOOST_DESCRIBE_STRUCT( X2, (), (m) ) + +struct X3 +{ + int m; + explicit X3( int m_ ): m( m_ ) {} +}; + +BOOST_DESCRIBE_STRUCT( X3, (), (m) ) + +class Y: public X1, protected X2, private X3 +{ +public: + + int m1; + +protected: + + int m2; + +private: + + int m3; + +public: + + Y( int x1, int x2, int x3, int m1_, int m2_, int m3_ ): + X1( x1 ), X2( x2 ), X3( x3 ), m1( m1_ ), m2( m2_ ), m3( m3_ ) {} + + BOOST_DESCRIBE_CLASS( Y, (X1, X2, X3), (m1), (m2), (m3) ) +}; + +using boost::describe::operators::operator==; +using boost::describe::operators::operator!=; +using boost::describe::operators::operator<<; + +int main() +{ + Y y1( 0, 0, 0, 0, 0, 0 ); + + BOOST_TEST_EQ( y1, y1 ); + BOOST_TEST_EQ( boost::hash()(y1), boost::hash()(y1) ); + + Y y2( 1, 0, 0, 0, 0, 0 ); + + BOOST_TEST_NE( y1, y2 ); + BOOST_TEST_NE( boost::hash()(y1), boost::hash()(y2) ); + + Y y3( 0, 1, 0, 0, 0, 0 ); + + BOOST_TEST_NE( y1, y3 ); + BOOST_TEST_NE( boost::hash()(y1), boost::hash()(y3) ); + + BOOST_TEST_NE( y2, y3 ); + BOOST_TEST_NE( boost::hash()(y2), boost::hash()(y3) ); + + Y y4( 0, 0, 1, 0, 0, 0 ); + + BOOST_TEST_NE( y1, y4 ); + BOOST_TEST_NE( boost::hash()(y1), boost::hash()(y4) ); + + BOOST_TEST_NE( y2, y4 ); + BOOST_TEST_NE( boost::hash()(y2), boost::hash()(y4) ); + + BOOST_TEST_NE( y3, y4 ); + BOOST_TEST_NE( boost::hash()(y3), boost::hash()(y4) ); + + Y y5( 0, 0, 0, 1, 0, 0 ); + + BOOST_TEST_NE( y1, y5 ); + BOOST_TEST_NE( boost::hash()(y1), boost::hash()(y5) ); + + BOOST_TEST_NE( y2, y5 ); + BOOST_TEST_NE( boost::hash()(y2), boost::hash()(y5) ); + + BOOST_TEST_NE( y3, y5 ); + BOOST_TEST_NE( boost::hash()(y3), boost::hash()(y5) ); + + BOOST_TEST_NE( y4, y5 ); + BOOST_TEST_NE( boost::hash()(y4), boost::hash()(y5) ); + + Y y6( 0, 0, 0, 0, 1, 0 ); + + BOOST_TEST_NE( y1, y6 ); + BOOST_TEST_NE( boost::hash()(y1), boost::hash()(y6) ); + + BOOST_TEST_NE( y2, y6 ); + BOOST_TEST_NE( boost::hash()(y2), boost::hash()(y6) ); + + BOOST_TEST_NE( y3, y6 ); + BOOST_TEST_NE( boost::hash()(y3), boost::hash()(y6) ); + + BOOST_TEST_NE( y4, y6 ); + BOOST_TEST_NE( boost::hash()(y4), boost::hash()(y6) ); + + BOOST_TEST_NE( y5, y6 ); + BOOST_TEST_NE( boost::hash()(y5), boost::hash()(y6) ); + + Y y7( 0, 0, 0, 0, 0, 1 ); + + BOOST_TEST_NE( y1, y7 ); + BOOST_TEST_NE( boost::hash()(y1), boost::hash()(y7) ); + + BOOST_TEST_NE( y2, y7 ); + BOOST_TEST_NE( boost::hash()(y2), boost::hash()(y7) ); + + BOOST_TEST_NE( y3, y7 ); + BOOST_TEST_NE( boost::hash()(y3), boost::hash()(y7) ); + + BOOST_TEST_NE( y4, y7 ); + BOOST_TEST_NE( boost::hash()(y4), boost::hash()(y7) ); + + BOOST_TEST_NE( y5, y7 ); + BOOST_TEST_NE( boost::hash()(y5), boost::hash()(y7) ); + + BOOST_TEST_NE( y6, y7 ); + BOOST_TEST_NE( boost::hash()(y6), boost::hash()(y7) ); + + return boost::report_errors(); +} + +#endif