diff --git a/doc/hash.adoc b/doc/hash.adoc index 903841e..1a5ecde 100644 --- a/doc/hash.adoc +++ b/doc/hash.adoc @@ -22,6 +22,7 @@ include::hash/recent.adoc[] include::hash/tutorial.adoc[] include::hash/user.adoc[] include::hash/combine.adoc[] +include::hash/describe.adoc[] include::hash/reference.adoc[] include::hash/notes.adoc[] include::hash/links.adoc[] diff --git a/doc/hash/describe.adoc b/doc/hash/describe.adoc new file mode 100644 index 0000000..2e4b481 --- /dev/null +++ b/doc/hash/describe.adoc @@ -0,0 +1,61 @@ +//// +Copyright 2022 Peter Dimov +Distributed under the Boost Software License, Version 1.0. +https://www.boost.org/LICENSE_1_0.txt +//// + +[#describe] += Hashing User Types with Boost.Describe +:idprefix: describe_ + +Let's look at our `point` class again: + +[source] +---- +class point +{ + int x; + int y; + +public: + + point() : x(0), y(0) {} + point(int x, int y) : x(x), y(y) {} +}; +---- + +If you're using {cpp}14 or above, a much easier way to add +support for `boost::hash` to `point` is by using +link:../../../describe/index.html[Boost.Describe] (and +get an automatic definition of `operator==` for free): + +[source] +---- + +#include +#include + +class point +{ + int x; + int y; + + BOOST_DESCRIBE_CLASS(point, (), (), (), (x, y)) + +public: + + point() : x(0), y(0) {} + point(int x, int y) : x(x), y(y) {} +}; + +using boost::describe::operators::operator==; +using boost::describe::operators::operator!=; +---- + +(Full code for this example is at +link:../../examples/point2.cpp[examples/point2.cpp].) + +Since the `point` class has been annotated with `BOOST_DESCRIBE_CLASS`, +the library can enumerate its members (and base classes) and automatically +synthesize the appropriate `hash_value` overload for it, without us needing +to do so. diff --git a/doc/hash/intro.adoc b/doc/hash/intro.adoc index 2203bdd..2e7adf2 100644 --- a/doc/hash/intro.adoc +++ b/doc/hash/intro.adoc @@ -32,6 +32,9 @@ Out of the box, `boost::hash` supports * unordered sequences, standard or user-defined (sequences for which the hash value does not depend on the element order, such as `std::unordered_set` and `std::unordered_map`); +* described structs and classes -- ones that have been annotated with the + `BOOST_DESCRIBE_STRUCT` or `BOOST_DESCRIBE_CLASS` macros from + link:../../../describe/index.html[Boost.Describe]; * `std::unique_ptr`, `std::shared_ptr`; * `std::type_index`; * `std::error_code`, `std::error_condition`; diff --git a/doc/hash/recent.adoc b/doc/hash/recent.adoc index 79efcb6..0ad37d1 100644 --- a/doc/hash/recent.adoc +++ b/doc/hash/recent.adoc @@ -21,6 +21,9 @@ Major update. * User-defined containers (types that have `begin()` and `end()` member functions that return iterators) are now supported out of the box. +* Described structs and classes (those annotated with + `BOOST_DESCRIBE_STRUCT` or `BOOST_DESCRIBE_CLASS`) are now + supported out of the box. * `hash_combine` has been improved. * The performance (and quality, as a result of the above change) of string hashing has been improved. `boost::hash` for strings diff --git a/doc/hash/reference.adoc b/doc/hash/reference.adoc index 432fe25..957dd69 100644 --- a/doc/hash/reference.adoc +++ b/doc/hash/reference.adoc @@ -29,6 +29,7 @@ namespace container_hash template struct is_range; template struct is_contiguous_range; template struct is_unordered_range; +template struct is_described_class; } // namespace container_hash @@ -103,6 +104,10 @@ template template std::size_t hash_value( T const& v ); +// Enabled only when container_hash::is_described_class::value is true +template + std::size_t hash_value( T const& v ); + template std::size_t hash_value( std::shared_ptr const& v ); @@ -446,6 +451,34 @@ Remarks: :: This overload handles the standard unordered containers, such as `std::unordered_set` and `std::unordered_map`. +[source] +---- +// Enabled only when container_hash::is_described_class::value is true +template + std::size_t hash_value( T const& v ); +---- + +Effects: :: ++ +[source] +---- +std::size_t seed = 0; + +boost::hash_combine( seed, b1 ); +boost::hash_combine( seed, b2 ); +// ... +boost::hash_combine( seed, bM ); + +boost::hash_combine( seed, m1 ); +boost::hash_combine( seed, m2 ); +// ... +boost::hash_combine( seed, mN ); + +return seed; +---- ++ +where `bi` are the bases of `v` and `mi` are its members. + [source] ---- template @@ -633,3 +666,40 @@ template struct is_unordered_range Users are allowed to specialize `is_unordered_range` for their types if the default behavior does not deduce the correct value. + +== + +Defines the trait `boost::container_hash::is_described_class`. + +[source] +---- +namespace boost +{ + +namespace container_hash +{ + +template struct is_described_class; + +} // namespace container_hash + +} // namespace boost +---- + +=== is_described_class + +[source] +---- +template struct is_described_class +{ + static constexpr bool value = /* see below */; +}; +---- + +`is_described_class::value` is `true` when +`boost::describe::has_describe_bases::value` is `true`, +`boost::describe::has_describe_members::value` is `true`, and +`T` is not a union. + +Users are allowed to specialize `is_described_class` for their types +if the default behavior does not deduce the correct value. diff --git a/doc/hash/user.adoc b/doc/hash/user.adoc index e0f72ad..fe0212e 100644 --- a/doc/hash/user.adoc +++ b/doc/hash/user.adoc @@ -74,7 +74,7 @@ assert(books.find(knife) != books.end()); assert(books.find(dandelion) == books.end()); ---- -The full example can be found in: +The full example can be found in link:../../examples/books.hpp[examples/books.hpp] and link:../../examples/books.cpp[examples/books.cpp].