From 7444eee62b6ea8f06d575d961e615ee4df17e93b Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 22 Apr 2012 19:46:28 +0000 Subject: [PATCH] Hash: Support std::array and std::tuple. Refs #6806. [SVN r78144] --- hash/doc/changes.qbk | 2 + hash/doc/ref.xml | 28 +++++ hash/test/Jamfile.v2 | 2 + hash/test/hash_map_test.cpp | 2 +- hash/test/hash_std_array_test.cpp | 103 ++++++++++++++++++ hash/test/hash_std_tuple_test.cpp | 77 +++++++++++++ .../hash/detail/container_fwd_0x.hpp | 41 +++++++ include/boost/functional/hash/extensions.hpp | 89 +++++++++++++-- 8 files changed, 333 insertions(+), 11 deletions(-) create mode 100644 hash/test/hash_std_array_test.cpp create mode 100644 hash/test/hash_std_tuple_test.cpp create mode 100644 include/boost/functional/hash/detail/container_fwd_0x.hpp diff --git a/hash/doc/changes.qbk b/hash/doc/changes.qbk index 41d6511..1673b34 100644 --- a/hash/doc/changes.qbk +++ b/hash/doc/changes.qbk @@ -126,5 +126,7 @@ [h2 Boost 1.50.0] * Avoid gcc's `-Wfloat-equal` warning. +* [@http://svn.boost.org/trac/boost/ticket/6806 Ticket 6806]: + Support `std::array` and `std::tuple` when available. [endsect] diff --git a/hash/doc/ref.xml b/hash/doc/ref.xml index 318bf85..9256e98 100644 --- a/hash/doc/ref.xml +++ b/hash/doc/ref.xml @@ -744,6 +744,25 @@ for(; first != last; ++first) std::type_index + + + std::size_t + std::array<T, N> const& + + + + + std::size_t + std::tuple<T...> + + Generally shouldn't be called directly by users, instead they should use boost::hash, boost::hash_range @@ -810,6 +829,7 @@ for(; first != last; ++first) std::multiset<K, C, A>, std::map<K, T, C, A>, std::multimap<K, T, C, A> + std::array<T, N> hash_range(val.begin(), val.end()) @@ -818,6 +838,14 @@ for(; first != last; ++first) size_t seed = 0; hash_combine(seed, val.first); hash_combine(seed, val.second); +return seed; + + + std::tuple<T...> + size_t seed = 0; +hash_combine(seed, get<0>(val)); +hash_combine(seed, get<1>(val)); +// .... return seed; diff --git a/hash/test/Jamfile.v2 b/hash/test/Jamfile.v2 index cf64ece..af24329 100644 --- a/hash/test/Jamfile.v2 +++ b/hash/test/Jamfile.v2 @@ -40,6 +40,8 @@ test-suite functional/hash [ run hash_map_test.cpp : : : BOOST_HASH_NO_IMPLICIT_CASTS ] [ run hash_complex_test.cpp : : : BOOST_HASH_NO_IMPLICIT_CASTS ] [ run hash_type_index_test.cpp : : : BOOST_HASH_NO_IMPLICIT_CASTS ] + [ run hash_std_array_test.cpp : : : BOOST_HASH_NO_IMPLICIT_CASTS ] + [ run hash_std_tuple_test.cpp : : : BOOST_HASH_NO_IMPLICIT_CASTS ] [ run link_test.cpp link_test_2.cpp : : : BOOST_HASH_NO_IMPLICIT_CASTS ] [ run link_ext_test.cpp link_no_ext_test.cpp : : : BOOST_HASH_NO_IMPLICIT_CASTS ] [ run extensions_hpp_test.cpp : : : BOOST_HASH_NO_IMPLICIT_CASTS ] diff --git a/hash/test/hash_map_test.cpp b/hash/test/hash_map_test.cpp index 2f813c3..7e117c7 100644 --- a/hash/test/hash_map_test.cpp +++ b/hash/test/hash_map_test.cpp @@ -27,7 +27,7 @@ using std::multimap; #define CONTAINER_TYPE multimap #include "./hash_map_test.hpp" -#endif // TEST_EXTENSTIONS +#endif // TEST_EXTENSIONS int main() { diff --git a/hash/test/hash_std_array_test.cpp b/hash/test/hash_std_array_test.cpp new file mode 100644 index 0000000..5e8a3d4 --- /dev/null +++ b/hash/test/hash_std_array_test.cpp @@ -0,0 +1,103 @@ + +// Copyright 2012 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) + +#include "./config.hpp" + +#ifdef TEST_EXTENSIONS +# ifdef TEST_STD_INCLUDES +# include +# else +# include +# endif +#endif + +#include +#include + +#if defined(TEST_EXTENSIONS) && !defined(BOOST_NO_0X_HDR_ARRAY) +#define TEST_ARRAY +#include +#include +#endif + +#ifdef TEST_ARRAY + +template +void array_tests(T const& v) { + boost::hash hf; + for(typename T::const_iterator i = v.begin(); i != v.end(); ++i) { + for(typename T::const_iterator j = v.begin(); j != v.end(); ++j) { + if (i != j) + BOOST_TEST(hf(*i) != hf(*j)); + else + BOOST_TEST(hf(*i) == hf(*j)); + } + } +} + +void empty_array_test() { +/* + boost::hash > empty_array_hash; + std::array empty_array; + BOOST_TEST(empty_array_hash(empty_array) == boost::hash_value(empty_array)); +*/ +} + +void int_1_array_test() +{ + std::vector > arrays; + std::array val; + val[0] = 0; + arrays.emplace_back(val); + val[0] = 1; + arrays.emplace_back(val); + val[0] = 2; + arrays.emplace_back(val); + array_tests(arrays); +} + +void string_1_array_test() +{ + std::vector > arrays; + std::array val; + arrays.emplace_back(val); + val[0] = "one"; + arrays.emplace_back(val); + val[0] = "two"; + arrays.emplace_back(val); + array_tests(arrays); +} + +void string_3_array_test() +{ + std::vector > arrays; + std::array val; + arrays.emplace_back(val); + val[0] = "one"; + arrays.emplace_back(val); + val[0] = ""; val[1] = "one"; val[2] = ""; + arrays.emplace_back(val); + val[0] = ""; val[1] = ""; val[2] = "one"; + arrays.emplace_back(val); + val[0] = "one"; val[1] = "one"; val[2] = "one"; + arrays.emplace_back(val); + val[0] = "one"; val[1] = "two"; val[2] = "three"; + arrays.emplace_back(val); + array_tests(arrays); +} + +#endif // TEST_ARRAY + +int main() +{ +#ifdef TEST_ARRAY + empty_array_test(); + int_1_array_test(); + string_1_array_test(); + string_3_array_test(); +#endif + + return boost::report_errors(); +} diff --git a/hash/test/hash_std_tuple_test.cpp b/hash/test/hash_std_tuple_test.cpp new file mode 100644 index 0000000..84aae75 --- /dev/null +++ b/hash/test/hash_std_tuple_test.cpp @@ -0,0 +1,77 @@ + +// Copyright 2012 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) + +#include "./config.hpp" + +#ifdef TEST_EXTENSIONS +# ifdef TEST_STD_INCLUDES +# include +# else +# include +# endif +#endif + +#include +#include + +#if defined(TEST_EXTENSIONS) && !defined(BOOST_NO_0X_HDR_TUPLE) +#define TEST_TUPLE +#include +#include +#endif + +#ifdef TEST_TUPLE + +template +void tuple_tests(T const& v) { + boost::hash hf; + for(typename T::const_iterator i = v.begin(); i != v.end(); ++i) { + for(typename T::const_iterator j = v.begin(); j != v.end(); ++j) { + if (i != j) + BOOST_TEST(hf(*i) != hf(*j)); + else + BOOST_TEST(hf(*i) == hf(*j)); + } + } +} + +void empty_tuple_test() { + boost::hash > empty_tuple_hash; + std::tuple<> empty_tuple; + BOOST_TEST(empty_tuple_hash(empty_tuple) == boost::hash_value(empty_tuple)); +} + +void int_tuple_test() { + std::vector > int_tuples; + int_tuples.push_back(std::make_tuple(0)); + int_tuples.push_back(std::make_tuple(1)); + int_tuples.push_back(std::make_tuple(2)); + tuple_tests(int_tuples); +} + +void int_string_tuple_test() { + std::vector > int_string_tuples; + int_string_tuples.push_back(std::make_tuple(0, "zero")); + int_string_tuples.push_back(std::make_tuple(1, "one")); + int_string_tuples.push_back(std::make_tuple(2, "two")); + int_string_tuples.push_back(std::make_tuple(0, "one")); + int_string_tuples.push_back(std::make_tuple(1, "zero")); + int_string_tuples.push_back(std::make_tuple(0, "")); + int_string_tuples.push_back(std::make_tuple(1, "")); + tuple_tests(int_string_tuples); +} + +#endif // TEST_TUPLE + +int main() +{ +#ifdef TEST_TUPLE + empty_tuple_test(); + int_tuple_test(); + int_string_tuple_test(); +#endif + + return boost::report_errors(); +} diff --git a/include/boost/functional/hash/detail/container_fwd_0x.hpp b/include/boost/functional/hash/detail/container_fwd_0x.hpp new file mode 100644 index 0000000..566f2af --- /dev/null +++ b/include/boost/functional/hash/detail/container_fwd_0x.hpp @@ -0,0 +1,41 @@ + +// Copyright 2012 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) + +#if !defined(BOOST_DETAIL_CONTAINER_FWD_0X_HPP) +#define BOOST_DETAIL_CONTAINER_FWD_0X_HPP + +#include + +// std::array + +#if !defined(BOOST_NO_0X_HDR_ARRAY) + // Don't forward declare std::array for Dinkumware, as it seems to be + // just 'using std::tr1::array'. +# if (defined(BOOST_DETAIL_NO_CONTAINER_FWD) && \ + !defined(BOOST_DETAIL_TEST_FORCE_CONTAINER_FWD)) || \ + (defined(_YVALS) && !defined(__IBMCPP__)) || defined(_CPPLIB_VER) +# include +# else +namespace std { + template class array; +} +# endif +#endif + +// std::tuple + +#if !defined(BOOST_NO_0X_HDR_TUPLE) +# if (defined(BOOST_DETAIL_NO_CONTAINER_FWD) && \ + !defined(BOOST_DETAIL_TEST_FORCE_CONTAINER_FWD)) || \ + defined(BOOST_NO_VARIADIC_TEMPLATES) +# include +# else +namespace std { + template class tuple; +} +# endif +#endif + +#endif diff --git a/include/boost/functional/hash/extensions.hpp b/include/boost/functional/hash/extensions.hpp index 3c587a3..1d873ed 100644 --- a/include/boost/functional/hash/extensions.hpp +++ b/include/boost/functional/hash/extensions.hpp @@ -14,7 +14,11 @@ #define BOOST_FUNCTIONAL_HASH_EXTENSIONS_HPP #include -#include +#include +#include +#include +#include +#include #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once @@ -54,51 +58,51 @@ namespace boost std::size_t hash_value(std::pair const& v) { std::size_t seed = 0; - hash_combine(seed, v.first); - hash_combine(seed, v.second); + boost::hash_combine(seed, v.first); + boost::hash_combine(seed, v.second); return seed; } template std::size_t hash_value(std::vector const& v) { - return hash_range(v.begin(), v.end()); + return boost::hash_range(v.begin(), v.end()); } template std::size_t hash_value(std::list const& v) { - return hash_range(v.begin(), v.end()); + return boost::hash_range(v.begin(), v.end()); } template std::size_t hash_value(std::deque const& v) { - return hash_range(v.begin(), v.end()); + return boost::hash_range(v.begin(), v.end()); } template std::size_t hash_value(std::set const& v) { - return hash_range(v.begin(), v.end()); + return boost::hash_range(v.begin(), v.end()); } template std::size_t hash_value(std::multiset const& v) { - return hash_range(v.begin(), v.end()); + return boost::hash_range(v.begin(), v.end()); } template std::size_t hash_value(std::map const& v) { - return hash_range(v.begin(), v.end()); + return boost::hash_range(v.begin(), v.end()); } template std::size_t hash_value(std::multimap const& v) { - return hash_range(v.begin(), v.end()); + return boost::hash_range(v.begin(), v.end()); } template @@ -110,6 +114,71 @@ namespace boost return seed; } +#if !defined(BOOST_NO_0X_HDR_ARRAY) + template + std::size_t hash_value(std::array const& v) + { + return boost::hash_range(v.begin(), v.end()); + } +#endif + +#if !defined(BOOST_NO_0X_HDR_TUPLE) + namespace hash_detail { + template + inline typename boost::enable_if_c<(I == std::tuple_size::value), + void>::type + hash_combine_tuple(std::size_t&, T const&) + { + } + + template + inline typename boost::enable_if_c<(I < std::tuple_size::value), + void>::type + hash_combine_tuple(std::size_t& seed, T const& v) + { + boost::hash_combine(seed, std::get(v)); + boost::hash_detail::hash_combine_tuple(seed, v); + } + + template + inline std::size_t hash_tuple(T const& v) + { + std::size_t seed = 0; + boost::hash_detail::hash_combine_tuple<0>(seed, v); + return seed; + } + } + +#if !defined(BOOST_NO_VARIADIC_TEMPLATES) + template + inline std::size_t hash_value(std::tuple const& v) + { + return boost::hash_detail::hash_tuple(v); + } +#else + + inline std::size_t hash_value(std::tuple<> const& v) + { + return boost::hash_detail::hash_tuple(v); + } + +# define BOOST_HASH_TUPLE_F(z, n, _) \ + template< \ + BOOST_PP_ENUM_PARAMS_Z(z, n, typename A) \ + > \ + inline std::size_t hash_value(std::tuple< \ + BOOST_PP_ENUM_PARAMS_Z(z, n, A) \ + > const& v) \ + { \ + return boost::hash_detail::hash_tuple(v); \ + } + + BOOST_PP_REPEAT_FROM_TO(1, 11, BOOST_HASH_TUPLE_F, _) +# undef BOOST_HASH_TUPLE_F +#endif + +#endif + // // call_hash_impl //