diff --git a/doc/sequence.qbk b/doc/sequence.qbk index 9e27f1c8..2fc6f4c2 100644 --- a/doc/sequence.qbk +++ b/doc/sequence.qbk @@ -1,6 +1,7 @@ [/============================================================================== Copyright (C) 2001-2011 Joel de Guzman Copyright (C) 2006 Dan Marsden + Copyright (C) 2014 Christoph Weiss Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -1896,6 +1897,87 @@ compile time error. [endsect] +[section Hashing] + +Automatically create a [classref boost::hash] conforming `hash_value` function. + +[heading Synopsis] + + template + std::size_t + hash_value(Seq const& seq); + +[heading Parameters] + +[table + [[Parameter] [Requirement] [Description]] + [[`seq`] [Instance of __sequence__] [__sequence__ to compute hash value of]] +] + +[*Return type]: `std::size_t` + +[*Requirements]: + +For each element `e` in sequence `seq`, `hash_value(seq)` is a valid expression +returning a type that is convertible to `std::size_t`. + +[*Semantics]: Returns a combined hash value for all elements of `seq`. + +[heading Header] + + #include + #include + +[heading Example] + + #include + #include + #include + #include + + void foo() + { + typedef boost::fusion::vector Vec; + const Vec v = {42, "Hello World", 't'}; + // Compute a hash value directly. + std::cout << "hash_value(v) = " << boost::fusion::hash_value(v) << '\n'; + // Or use it to create an unordered_map. + boost::unordered_map map; + map[v] = true; + assert(map.size() == 1 && map.count(v) == 1); + } + +[heading Example] + + #include + #include + #include + #include + + // We would like to define a struct that we can form unordered_sets of. + BOOST_FUSION_DEFINE_STRUCT( + (demo), Key, + (bool, b) + (std::string, s) + (int, i) + ) + + namespace demo { + // Make operator== and hash_value ADL accessible. + using boost::fusion::operator==; + using boost::fusion::hash_value; + typedef boost::unordered_set Set; + } + + void foo() + { + demo::Set set; + demo::Key key; + assert(set.count(key) == 0); + } + +[endsect] + [endsect] [endsect] diff --git a/include/boost/fusion/include/hash.hpp b/include/boost/fusion/include/hash.hpp new file mode 100644 index 00000000..8f483fc6 --- /dev/null +++ b/include/boost/fusion/include/hash.hpp @@ -0,0 +1,12 @@ +/*============================================================================= + Copyright (c) 2014 Christoph Weiss + + 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) +==============================================================================*/ +#if !defined(FUSION_INCLUDE_HASH) +#define FUSION_INCLUDE_HASH + +#include + +#endif diff --git a/include/boost/fusion/sequence/hash.hpp b/include/boost/fusion/sequence/hash.hpp new file mode 100644 index 00000000..53430961 --- /dev/null +++ b/include/boost/fusion/sequence/hash.hpp @@ -0,0 +1,42 @@ +/*============================================================================= + Copyright (c) 2014 Christoph Weiss + + 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) +==============================================================================*/ +#if !defined(FUSION_HASH_23072014_1017) +#define FUSION_HASH_23072014_1017 + +#include +#include +#include +#include + +namespace boost { namespace fusion +{ + namespace hashing + { + struct hash_combine_fold + { + typedef std::size_t result_type; + template + inline std::size_t operator()(std::size_t seed, T const& v) + { + boost::hash_combine(seed, v); + return seed; + } + }; + + template + inline typename + boost::enable_if, std::size_t>::type + hash_value(Seq const& seq) + { + return fold(seq, 0, hash_combine_fold()); + } + } + + using hashing::hash_value; +}} + +#endif diff --git a/test/Jamfile b/test/Jamfile index 73c4e89a..e89c3017 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -63,6 +63,7 @@ project [ run sequence/boost_tuple.cpp : : : : ] [ run sequence/cons.cpp : : : : ] [ run sequence/filter_view.cpp : : : : ] + [ run sequence/hash.cpp : : : : ] [ run sequence/io.cpp : : : : ] [ run sequence/iterator_range.cpp : : : : ] [ run sequence/joint_view.cpp : : : : ] @@ -70,6 +71,7 @@ project [ run sequence/list_construction.cpp : : : : ] [ run sequence/list_copy.cpp : : : : ] [ run sequence/list_iterator.cpp : : : : ] + [ run sequence/list_hash.cpp : : : : ] [ run sequence/list_make.cpp : : : : ] [ run sequence/list_misc.cpp : : : : ] [ run sequence/list_mutate.cpp : : : : ] @@ -79,6 +81,7 @@ project [ run sequence/deque_construction.cpp : : : : ] [ run sequence/deque_copy.cpp : : : : ] [ run sequence/deque_iterator.cpp : : : : ] + [ run sequence/deque_hash.cpp : : : : ] [ run sequence/deque_make.cpp : : : : ] [ run sequence/deque_misc.cpp : : : : ] [ run sequence/deque_move.cpp : : : : ] @@ -112,6 +115,7 @@ project [ run sequence/tuple_make.cpp : : : : ] [ run sequence/tuple_misc.cpp : : : : ] [ run sequence/tuple_mutate.cpp : : : : ] + [ run sequence/tuple_hash.cpp : : : : ] [ run sequence/tuple_tie.cpp : : : : ] [ run sequence/tr1_tuple_auto_conv.cpp : : : : ] [ run sequence/transform_view.cpp : : : : ] @@ -124,6 +128,7 @@ project [ run sequence/vector_move.cpp : : : : ] [ run sequence/vector_mutate.cpp : : : : ] [ run sequence/vector_n.cpp : : : : ] + [ run sequence/vector_hash.cpp : : : : ] [ run sequence/vector_tie.cpp : : : : ] [ run sequence/vector_value_at.cpp : : : : ] [ run sequence/zip_view.cpp : : : : ] diff --git a/test/sequence/deque_hash.cpp b/test/sequence/deque_hash.cpp new file mode 100644 index 00000000..01b36660 --- /dev/null +++ b/test/sequence/deque_hash.cpp @@ -0,0 +1,16 @@ +/*============================================================================= + Copyright (c) 2014 Christoph Weiss + + 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) +==============================================================================*/ +#include + +#define FUSION_SEQUENCE deque +#include "hash.hpp" + +int main() +{ + hash_test(); + return boost::report_errors(); +} diff --git a/test/sequence/hash.cpp b/test/sequence/hash.cpp new file mode 100644 index 00000000..b1692bf7 --- /dev/null +++ b/test/sequence/hash.cpp @@ -0,0 +1,58 @@ +/*============================================================================= + Copyright (c) 2014 Christoph Weiss + + 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) +==============================================================================*/ + +#include + +#include +#include +#include + +struct test_struct +{ + test_struct(bool bb, int ii, char cc, std::string const& ss) : + b(bb), + i(ii), + c(cc), + s(ss) {} + + bool b; + int i; + char c; + std::string s; +}; + +BOOST_FUSION_ADAPT_STRUCT( + test_struct, + (bool, b) + (int, i) + (char, c) + (std::string, s) +) + +int main() +{ + using boost::fusion::hash_value; + + const test_struct a0(false, 1, 'c', "Hello Nurse"), + a1(false, 1, 'c', "Hello Nurse"), + b(true, 1, 'c', "Hello Nurse"), + c(false, 0, 'c', "Hello Nurse"), + d(false, 1, 'd', "Hello Nurse"), + e(false, 1, 'c', "Hello World"); + + BOOST_TEST(hash_value(a0) == hash_value(a1)); + BOOST_TEST(hash_value(a0) != hash_value(b)); + BOOST_TEST(hash_value(a0) != hash_value(c)); + BOOST_TEST(hash_value(a0) != hash_value(d)); + BOOST_TEST(hash_value(a0) != hash_value(e)); + BOOST_TEST(hash_value(b) != hash_value(c)); + BOOST_TEST(hash_value(b) != hash_value(d)); + BOOST_TEST(hash_value(b) != hash_value(d)); + BOOST_TEST(hash_value(c) != hash_value(d)); + BOOST_TEST(hash_value(c) != hash_value(e)); + BOOST_TEST(hash_value(d) != hash_value(e)); +} diff --git a/test/sequence/hash.hpp b/test/sequence/hash.hpp new file mode 100644 index 00000000..7dea0a3f --- /dev/null +++ b/test/sequence/hash.hpp @@ -0,0 +1,34 @@ +/*============================================================================= + Copyright (c) 2014 Christoph Weiss + + 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) +==============================================================================*/ +#include +#include + +#include +#include +#include + +void +hash_test() +{ + using namespace boost::fusion; + + const FUSION_SEQUENCE v0(42, 'x', false, "Aurea prima"); + const FUSION_SEQUENCE v1(42, 'x', false, "Aurea prima"); + BOOST_TEST(hash_value(v0) == hash_value(v1)); + + const FUSION_SEQUENCE w(41, 'x', false, "Aurea prima"); + BOOST_TEST(hash_value(w) != hash_value(v0)); + + const FUSION_SEQUENCE x(42, 'y', false, "Aurea prima"); + BOOST_TEST(hash_value(x) != hash_value(v0)); + + const FUSION_SEQUENCE y(42, 'x', true, "Aurea prima"); + BOOST_TEST(hash_value(y) != hash_value(v0)); + + const FUSION_SEQUENCE z(42, 'x', false, "quae vindice nullo"); + BOOST_TEST(hash_value(z) != hash_value(v0)); +} diff --git a/test/sequence/list_hash.cpp b/test/sequence/list_hash.cpp new file mode 100644 index 00000000..51203b27 --- /dev/null +++ b/test/sequence/list_hash.cpp @@ -0,0 +1,16 @@ +/*============================================================================= + Copyright (c) 2014 Christoph Weiss + + 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) +==============================================================================*/ +#include + +#define FUSION_SEQUENCE list +#include "hash.hpp" + +int main() +{ + hash_test(); + return boost::report_errors(); +} diff --git a/test/sequence/tuple_hash.cpp b/test/sequence/tuple_hash.cpp new file mode 100644 index 00000000..e8f604ef --- /dev/null +++ b/test/sequence/tuple_hash.cpp @@ -0,0 +1,16 @@ +/*============================================================================= + Copyright (c) 2014 Christoph Weiss + + 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) +==============================================================================*/ +#include + +#define FUSION_SEQUENCE tuple +#include "hash.hpp" + +int main() +{ + hash_test(); + return boost::report_errors(); +} diff --git a/test/sequence/vector_hash.cpp b/test/sequence/vector_hash.cpp new file mode 100644 index 00000000..6b6dcd24 --- /dev/null +++ b/test/sequence/vector_hash.cpp @@ -0,0 +1,16 @@ +/*============================================================================= + Copyright (c) 2014 Christoph Weiss + + 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) +==============================================================================*/ +#include + +#define FUSION_SEQUENCE vector +#include "hash.hpp" + +int main() +{ + hash_test(); + return boost::report_errors(); +}