Hash: Support std::array and std::tuple. Refs #6806.

[SVN r78144]
This commit is contained in:
Daniel James
2012-04-22 19:46:28 +00:00
parent 5e3ec9012e
commit 7444eee62b
8 changed files with 333 additions and 11 deletions

View File

@@ -126,5 +126,7 @@
[h2 Boost 1.50.0] [h2 Boost 1.50.0]
* Avoid gcc's `-Wfloat-equal` warning. * 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] [endsect]

View File

@@ -744,6 +744,25 @@ for(; first != last; ++first)
<parameter name="val"><paramtype>std::type_index</paramtype></parameter> <parameter name="val"><paramtype>std::type_index</paramtype></parameter>
</signature> </signature>
<signature>
<template>
<template-type-parameter name="T"/>
<template-nontype-parameter name="N">
<type>std::size_t</type>
</template-nontype-parameter>
</template>
<type>std::size_t</type>
<parameter name="val"><paramtype>std::array&lt;T, N&gt; const&amp;</paramtype></parameter>
</signature>
<signature>
<template>
<template-type-parameter name="T" pack="1"/>
</template>
<type>std::size_t</type>
<parameter name="val"><paramtype>std::tuple&lt;T...&gt;</paramtype></parameter>
</signature>
<description><para> <description><para>
Generally shouldn't be called directly by users, instead they should use Generally shouldn't be called directly by users, instead they should use
<classname>boost::hash</classname>, <functionname>boost::hash_range</functionname> <classname>boost::hash</classname>, <functionname>boost::hash_range</functionname>
@@ -810,6 +829,7 @@ for(; first != last; ++first)
<code>std::multiset&lt;K,&#160;C,&#160;A&gt;</code>, <code>std::multiset&lt;K,&#160;C,&#160;A&gt;</code>,
<code>std::map&lt;K,&#160;T,&#160;C,&#160;A&gt;</code>, <code>std::map&lt;K,&#160;T,&#160;C,&#160;A&gt;</code>,
<code>std::multimap&lt;K,&#160;T,&#160;C,&#160;A&gt;</code> <code>std::multimap&lt;K,&#160;T,&#160;C,&#160;A&gt;</code>
<code>std::array&lt;T,&#160;N&gt;</code>
</entry> </entry>
<entry><code>hash_range(val.begin(), val.end())</code></entry> <entry><code>hash_range(val.begin(), val.end())</code></entry>
</row> </row>
@@ -818,6 +838,14 @@ for(; first != last; ++first)
<entry><programlisting>size_t seed = 0; <entry><programlisting>size_t seed = 0;
<functionname>hash_combine</functionname>(seed, val.first); <functionname>hash_combine</functionname>(seed, val.first);
<functionname>hash_combine</functionname>(seed, val.second); <functionname>hash_combine</functionname>(seed, val.second);
return seed;</programlisting></entry>
</row>
<row>
<entry><code>std::tuple&lt;T...&gt;</code></entry>
<entry><programlisting>size_t seed = 0;
<functionname>hash_combine</functionname>(seed, get&lt;0&gt;(val));
<functionname>hash_combine</functionname>(seed, get&lt;1&gt;(val));
// ....
return seed;</programlisting></entry> return seed;</programlisting></entry>
</row> </row>
<row> <row>

View File

@@ -40,6 +40,8 @@ test-suite functional/hash
[ run hash_map_test.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ] [ run hash_map_test.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ]
[ run hash_complex_test.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ] [ run hash_complex_test.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ]
[ run hash_type_index_test.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ] [ run hash_type_index_test.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ]
[ run hash_std_array_test.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ]
[ run hash_std_tuple_test.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ]
[ run link_test.cpp link_test_2.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ] [ run link_test.cpp link_test_2.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ]
[ run link_ext_test.cpp link_no_ext_test.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ] [ run link_ext_test.cpp link_no_ext_test.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ]
[ run extensions_hpp_test.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ] [ run extensions_hpp_test.cpp : : : <define>BOOST_HASH_NO_IMPLICIT_CASTS ]

View File

@@ -27,7 +27,7 @@ using std::multimap;
#define CONTAINER_TYPE multimap #define CONTAINER_TYPE multimap
#include "./hash_map_test.hpp" #include "./hash_map_test.hpp"
#endif // TEST_EXTENSTIONS #endif // TEST_EXTENSIONS
int main() int main()
{ {

View File

@@ -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 <functional>
# else
# include <boost/functional/hash.hpp>
# endif
#endif
#include <boost/config.hpp>
#include <boost/detail/lightweight_test.hpp>
#if defined(TEST_EXTENSIONS) && !defined(BOOST_NO_0X_HDR_ARRAY)
#define TEST_ARRAY
#include <array>
#include <vector>
#endif
#ifdef TEST_ARRAY
template <typename T>
void array_tests(T const& v) {
boost::hash<typename T::value_type> 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<std::array<int, 0> > empty_array_hash;
std::array<int, 0> empty_array;
BOOST_TEST(empty_array_hash(empty_array) == boost::hash_value(empty_array));
*/
}
void int_1_array_test()
{
std::vector<std::array<int, 1> > arrays;
std::array<int, 1> 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<std::array<std::string, 1> > arrays;
std::array<std::string, 1> 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<std::array<std::string,3 > > arrays;
std::array<std::string, 3> 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();
}

View File

@@ -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 <functional>
# else
# include <boost/functional/hash.hpp>
# endif
#endif
#include <boost/config.hpp>
#include <boost/detail/lightweight_test.hpp>
#if defined(TEST_EXTENSIONS) && !defined(BOOST_NO_0X_HDR_TUPLE)
#define TEST_TUPLE
#include <tuple>
#include <vector>
#endif
#ifdef TEST_TUPLE
template <typename T>
void tuple_tests(T const& v) {
boost::hash<typename T::value_type> 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<std::tuple<> > 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<std::tuple<int> > 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<std::tuple<int, std::string> > 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();
}

View File

@@ -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 <boost/detail/container_fwd.hpp>
// 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 <array>
# else
namespace std {
template <class, std::size_t> 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 <tuple>
# else
namespace std {
template <typename...> class tuple;
}
# endif
#endif
#endif

View File

@@ -14,7 +14,11 @@
#define BOOST_FUNCTIONAL_HASH_EXTENSIONS_HPP #define BOOST_FUNCTIONAL_HASH_EXTENSIONS_HPP
#include <boost/functional/hash/hash.hpp> #include <boost/functional/hash/hash.hpp>
#include <boost/detail/container_fwd.hpp> #include <boost/functional/hash/detail/container_fwd_0x.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/static_assert.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#if defined(_MSC_VER) && (_MSC_VER >= 1020) #if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once # pragma once
@@ -54,51 +58,51 @@ namespace boost
std::size_t hash_value(std::pair<A, B> const& v) std::size_t hash_value(std::pair<A, B> const& v)
{ {
std::size_t seed = 0; std::size_t seed = 0;
hash_combine(seed, v.first); boost::hash_combine(seed, v.first);
hash_combine(seed, v.second); boost::hash_combine(seed, v.second);
return seed; return seed;
} }
template <class T, class A> template <class T, class A>
std::size_t hash_value(std::vector<T, A> const& v) std::size_t hash_value(std::vector<T, A> const& v)
{ {
return hash_range(v.begin(), v.end()); return boost::hash_range(v.begin(), v.end());
} }
template <class T, class A> template <class T, class A>
std::size_t hash_value(std::list<T, A> const& v) std::size_t hash_value(std::list<T, A> const& v)
{ {
return hash_range(v.begin(), v.end()); return boost::hash_range(v.begin(), v.end());
} }
template <class T, class A> template <class T, class A>
std::size_t hash_value(std::deque<T, A> const& v) std::size_t hash_value(std::deque<T, A> const& v)
{ {
return hash_range(v.begin(), v.end()); return boost::hash_range(v.begin(), v.end());
} }
template <class K, class C, class A> template <class K, class C, class A>
std::size_t hash_value(std::set<K, C, A> const& v) std::size_t hash_value(std::set<K, C, A> const& v)
{ {
return hash_range(v.begin(), v.end()); return boost::hash_range(v.begin(), v.end());
} }
template <class K, class C, class A> template <class K, class C, class A>
std::size_t hash_value(std::multiset<K, C, A> const& v) std::size_t hash_value(std::multiset<K, C, A> const& v)
{ {
return hash_range(v.begin(), v.end()); return boost::hash_range(v.begin(), v.end());
} }
template <class K, class T, class C, class A> template <class K, class T, class C, class A>
std::size_t hash_value(std::map<K, T, C, A> const& v) std::size_t hash_value(std::map<K, T, C, A> const& v)
{ {
return hash_range(v.begin(), v.end()); return boost::hash_range(v.begin(), v.end());
} }
template <class K, class T, class C, class A> template <class K, class T, class C, class A>
std::size_t hash_value(std::multimap<K, T, C, A> const& v) std::size_t hash_value(std::multimap<K, T, C, A> const& v)
{ {
return hash_range(v.begin(), v.end()); return boost::hash_range(v.begin(), v.end());
} }
template <class T> template <class T>
@@ -110,6 +114,71 @@ namespace boost
return seed; return seed;
} }
#if !defined(BOOST_NO_0X_HDR_ARRAY)
template <class T, std::size_t N>
std::size_t hash_value(std::array<T, N> const& v)
{
return boost::hash_range(v.begin(), v.end());
}
#endif
#if !defined(BOOST_NO_0X_HDR_TUPLE)
namespace hash_detail {
template <std::size_t I, typename T>
inline typename boost::enable_if_c<(I == std::tuple_size<T>::value),
void>::type
hash_combine_tuple(std::size_t&, T const&)
{
}
template <std::size_t I, typename T>
inline typename boost::enable_if_c<(I < std::tuple_size<T>::value),
void>::type
hash_combine_tuple(std::size_t& seed, T const& v)
{
boost::hash_combine(seed, std::get<I>(v));
boost::hash_detail::hash_combine_tuple<I + 1>(seed, v);
}
template <typename T>
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 <typename... T>
inline std::size_t hash_value(std::tuple<T...> 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 // call_hash_impl
// //