Boost logo

Boost-Commit :

From: daniel_james_at_[hidden]
Date: 2008-04-26 12:23:52


Author: danieljames
Date: 2008-04-26 12:23:51 EDT (Sat, 26 Apr 2008)
New Revision: 44779
URL: http://svn.boost.org/trac/boost/changeset/44779

Log:
Merge in support for equality operators.
Added:
   branches/unordered/trunk/libs/unordered/test/unordered/equality_tests.cpp (contents, props changed)
Text files modified:
   branches/unordered/trunk/boost/unordered/detail/hash_table_impl.hpp | 93 ++++++++++++++++++++++++++++++++++++++++
   branches/unordered/trunk/boost/unordered_map.hpp | 32 +++++++++++++
   branches/unordered/trunk/boost/unordered_set.hpp | 32 +++++++++++++
   branches/unordered/trunk/libs/unordered/test/objects/minimal.hpp | 24 ++++++++++
   branches/unordered/trunk/libs/unordered/test/unordered/Jamfile.v2 | 1
   branches/unordered/trunk/libs/unordered/test/unordered/compile_map.cpp | 37 +++++++++++++++
   branches/unordered/trunk/libs/unordered/test/unordered/compile_set.cpp | 30 ++++++++++++
   branches/unordered/trunk/libs/unordered/test/unordered/compile_tests.hpp | 18 ++++++-
   8 files changed, 262 insertions(+), 5 deletions(-)

Modified: branches/unordered/trunk/boost/unordered/detail/hash_table_impl.hpp
==============================================================================
--- branches/unordered/trunk/boost/unordered/detail/hash_table_impl.hpp (original)
+++ branches/unordered/trunk/boost/unordered/detail/hash_table_impl.hpp 2008-04-26 12:23:51 EDT (Sat, 26 Apr 2008)
@@ -2136,6 +2136,99 @@
                 }
             }
 
+ //
+ // equals
+ //
+
+private:
+#if BOOST_UNORDERED_EQUIVALENT_KEYS
+ static inline bool group_equals(link_ptr it1, link_ptr it2,
+ type_wrapper<key_type>*)
+ {
+ return data::group_count(it1) == data::group_count(it2);
+ }
+
+ static inline bool group_equals(link_ptr it1, link_ptr it2, void*)
+ {
+ if(!BOOST_UNORDERED_BORLAND_BOOL(it2)) return false;
+ link_ptr end1 = data::next_group(it1);
+ link_ptr end2 = data::next_group(it2);
+ do {
+ if(data::get_value(it1).second != data::get_value(it2).second) return false;
+ it1 = it1->next_;
+ it2 = it2->next_;
+ } while(it1 != end1 && it2 != end2);
+ return it1 == end1 && it2 == end2;
+ }
+#else
+ static inline bool group_equals(link_ptr it1, link_ptr it2,
+ type_wrapper<key_type>*)
+ {
+ return true;
+ }
+
+ static inline bool group_equals(link_ptr it1, link_ptr it2, void*)
+ {
+ return data::get_value(it1).second == data::get_value(it2).second;
+ }
+#endif
+
+public:
+ bool equals(BOOST_UNORDERED_TABLE const& other) const
+ {
+ if(size() != other.size()) return false;
+
+ for(bucket_ptr i = data_.cached_begin_bucket_,
+ j = data_.buckets_end(); i != j; ++i)
+ {
+ for(link_ptr it(i->next_); BOOST_UNORDERED_BORLAND_BOOL(it); it = data::next_group(it))
+ {
+ link_ptr other_pos = other.find_iterator(other.extract_key(data::get_value(it)));
+ if(!BOOST_UNORDERED_BORLAND_BOOL(other_pos) ||
+ !group_equals(it, other_pos, (type_wrapper<value_type>*)0))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ inline std::size_t group_hash(link_ptr it, type_wrapper<key_type>*) const
+ {
+ std::size_t seed = data::group_count(it);
+ std::size_t hashed_key = hash_function()(data::get_value(it));
+ boost::hash_combine(seed, hashed_key);
+ return seed;
+ }
+
+ inline std::size_t group_hash(link_ptr it, void*) const
+ {
+ std::size_t seed = hash_function()(data::get_value(it).first);
+
+ link_ptr end = data::next_group(it);
+
+ do {
+ boost::hash_combine(seed, data::get_value(it).second);
+ it = it->next_;
+ } while(it != end);
+
+ return seed;
+ }
+
+ std::size_t hash_value() const
+ {
+ std::size_t seed = 0;
+
+ for(bucket_ptr i = data_.cached_begin_bucket_,
+ j = data_.buckets_end(); i != j; ++i)
+ {
+ for(link_ptr it(i->next_); BOOST_UNORDERED_BORLAND_BOOL(it); it = data::next_group(it))
+ seed ^= group_hash(it, (type_wrapper<value_type>*)0);
+ }
+
+ return seed;
+ }
+
         private:
 
             // strong exception safety, no side effects

Modified: branches/unordered/trunk/boost/unordered_map.hpp
==============================================================================
--- branches/unordered/trunk/boost/unordered_map.hpp (original)
+++ branches/unordered/trunk/boost/unordered_map.hpp 2008-04-26 12:23:51 EDT (Sat, 26 Apr 2008)
@@ -18,8 +18,8 @@
 #include <functional>
 #include <memory>
 
-#include <boost/unordered/detail/hash_table.hpp>
 #include <boost/functional/hash.hpp>
+#include <boost/unordered/detail/hash_table.hpp>
 
 #if !defined(BOOST_HAS_RVALUE_REFS)
 #include <boost/unordered/detail/move.hpp>
@@ -387,6 +387,21 @@
         {
             base.rehash(n);
         }
+
+ friend bool operator==(unordered_map const& m1, unordered_map const& m2)
+ {
+ return m1.base.equals(m2.base);
+ }
+
+ friend bool operator!=(unordered_map const& m1, unordered_map const& m2)
+ {
+ return !m1.base.equals(m2.base);
+ }
+
+ friend std::size_t hash_value(unordered_map const& m)
+ {
+ return m.base.hash_value();
+ }
     }; // class template unordered_map
 
     template <class K, class T, class H, class P, class A>
@@ -739,6 +754,21 @@
         {
             base.rehash(n);
         }
+
+ friend bool operator==(unordered_multimap const& m1, unordered_multimap const& m2)
+ {
+ return m1.base.equals(m2.base);
+ }
+
+ friend bool operator!=(unordered_multimap const& m1, unordered_multimap const& m2)
+ {
+ return !m1.base.equals(m2.base);
+ }
+
+ friend std::size_t hash_value(unordered_multimap const& m)
+ {
+ return m.base.hash_value();
+ }
     }; // class template unordered_multimap
 
     template <class K, class T, class H, class P, class A>

Modified: branches/unordered/trunk/boost/unordered_set.hpp
==============================================================================
--- branches/unordered/trunk/boost/unordered_set.hpp (original)
+++ branches/unordered/trunk/boost/unordered_set.hpp 2008-04-26 12:23:51 EDT (Sat, 26 Apr 2008)
@@ -18,8 +18,8 @@
 #include <functional>
 #include <memory>
 
-#include <boost/unordered/detail/hash_table.hpp>
 #include <boost/functional/hash.hpp>
+#include <boost/unordered/detail/hash_table.hpp>
 
 #if !defined(BOOST_HAS_RVALUE_REFS)
 #include <boost/unordered/detail/move.hpp>
@@ -358,6 +358,21 @@
         {
             base.rehash(n);
         }
+
+ friend bool operator==(unordered_set const& m1, unordered_set const& m2)
+ {
+ return m1.base.equals(m2.base);
+ }
+
+ friend bool operator!=(unordered_set const& m1, unordered_set const& m2)
+ {
+ return !m1.base.equals(m2.base);
+ }
+
+ friend std::size_t hash_value(unordered_set const& m)
+ {
+ return m.base.hash_value();
+ }
     }; // class template unordered_set
 
     template <class T, class H, class P, class A>
@@ -695,6 +710,21 @@
         {
             base.rehash(n);
         }
+
+ friend bool operator==(unordered_multiset const& m1, unordered_multiset const& m2)
+ {
+ return m1.base.equals(m2.base);
+ }
+
+ friend bool operator!=(unordered_multiset const& m1, unordered_multiset const& m2)
+ {
+ return !m1.base.equals(m2.base);
+ }
+
+ friend std::size_t hash_value(unordered_multiset const& m)
+ {
+ return m.base.hash_value();
+ }
     }; // class template unordered_multiset
 
     template <class T, class H, class P, class A>

Modified: branches/unordered/trunk/libs/unordered/test/objects/minimal.hpp
==============================================================================
--- branches/unordered/trunk/libs/unordered/test/objects/minimal.hpp (original)
+++ branches/unordered/trunk/libs/unordered/test/objects/minimal.hpp 2008-04-26 12:23:51 EDT (Sat, 26 Apr 2008)
@@ -22,6 +22,7 @@
 namespace minimal
 {
     class copy_constructible;
+ class copy_constructible_equality_comparable;
     class default_copy_constructible;
     class assignable;
 
@@ -42,6 +43,29 @@
         copy_constructible() {}
     };
 
+ class copy_constructible_equality_comparable
+ {
+ public:
+ static copy_constructible_equality_comparable create() { return copy_constructible_equality_comparable(); }
+ copy_constructible_equality_comparable(copy_constructible_equality_comparable const&) {}
+ ~copy_constructible_equality_comparable() {}
+ private:
+ copy_constructible_equality_comparable& operator=(copy_constructible_equality_comparable const&);
+ copy_constructible_equality_comparable() {}
+ };
+
+ bool operator==(copy_constructible_equality_comparable, copy_constructible_equality_comparable) {
+ return true;
+ }
+
+ bool operator!=(copy_constructible_equality_comparable, copy_constructible_equality_comparable) {
+ return false;
+ }
+
+ std::size_t hash_value(copy_constructible_equality_comparable) {
+ return 1;
+ }
+
     class default_copy_constructible
     {
     public:

Modified: branches/unordered/trunk/libs/unordered/test/unordered/Jamfile.v2
==============================================================================
--- branches/unordered/trunk/libs/unordered/test/unordered/Jamfile.v2 (original)
+++ branches/unordered/trunk/libs/unordered/test/unordered/Jamfile.v2 2008-04-26 12:23:51 EDT (Sat, 26 Apr 2008)
@@ -33,5 +33,6 @@
         [ run bucket_tests.cpp ]
         [ run load_factor_tests.cpp ]
         [ run rehash_tests.cpp ]
+ [ run equality_tests.cpp ]
         [ run swap_tests.cpp : : : <define>BOOST_UNORDERED_SWAP_METHOD=2 ]
     ;

Modified: branches/unordered/trunk/libs/unordered/test/unordered/compile_map.cpp
==============================================================================
--- branches/unordered/trunk/libs/unordered/test/unordered/compile_map.cpp (original)
+++ branches/unordered/trunk/libs/unordered/test/unordered/compile_map.cpp 2008-04-26 12:23:51 EDT (Sat, 26 Apr 2008)
@@ -22,6 +22,9 @@
             test::minimal::copy_constructible::create());
 
     std::cout<<"Test unordered_map.\n";
+
+ boost::unordered_map<int, int> int_map;
+
     boost::unordered_map<
         test::minimal::assignable,
         test::minimal::copy_constructible,
@@ -29,9 +32,13 @@
         test::minimal::equal_to<test::minimal::assignable>,
         test::minimal::allocator<value_type> > map;
 
+ container_test(int_map, std::pair<int const, int>(0, 0));
     container_test(map, value);
 
     std::cout<<"Test unordered_multimap.\n";
+
+ boost::unordered_multimap<int, int> int_multimap;
+
     boost::unordered_multimap<
         test::minimal::assignable,
         test::minimal::copy_constructible,
@@ -39,9 +46,39 @@
         test::minimal::equal_to<test::minimal::assignable>,
         test::minimal::allocator<value_type> > multimap;
 
+ container_test(int_multimap, std::pair<int const, int>(0, 0));
     container_test(multimap, value);
 }
 
+UNORDERED_AUTO_TEST(equality_tests) {
+ typedef std::pair<test::minimal::assignable const,
+ test::minimal::copy_constructible> value_type;
+
+ boost::unordered_map<int, int> int_map;
+
+ boost::unordered_map<
+ test::minimal::assignable,
+ test::minimal::copy_constructible_equality_comparable,
+ test::minimal::hash<test::minimal::assignable>,
+ test::minimal::equal_to<test::minimal::assignable>,
+ test::minimal::allocator<value_type> > map;
+
+ equality_test(int_map);
+ equality_test(map);
+
+ boost::unordered_multimap<int, int> int_multimap;
+
+ boost::unordered_multimap<
+ test::minimal::assignable,
+ test::minimal::copy_constructible_equality_comparable,
+ test::minimal::hash<test::minimal::assignable>,
+ test::minimal::equal_to<test::minimal::assignable>,
+ test::minimal::allocator<value_type> > multimap;
+
+ equality_test(int_multimap);
+ equality_test(multimap);
+}
+
 UNORDERED_AUTO_TEST(test1) {
     boost::hash<int> hash;
     std::equal_to<int> equal_to;

Modified: branches/unordered/trunk/libs/unordered/test/unordered/compile_set.cpp
==============================================================================
--- branches/unordered/trunk/libs/unordered/test/unordered/compile_set.cpp (original)
+++ branches/unordered/trunk/libs/unordered/test/unordered/compile_set.cpp 2008-04-26 12:23:51 EDT (Sat, 26 Apr 2008)
@@ -18,24 +18,54 @@
     test::minimal::assignable assignable = test::minimal::assignable::create();
 
     std::cout<<"Test unordered_set.\n";
+ boost::unordered_set<int> int_set;
     boost::unordered_set<
         test::minimal::assignable,
         test::minimal::hash<test::minimal::assignable>,
         test::minimal::equal_to<test::minimal::assignable>,
         test::minimal::allocator<test::minimal::assignable> > set;
 
+ container_test(int_set, 0);
     container_test(set, assignable);
 
     std::cout<<"Test unordered_multiset.\n";
+ boost::unordered_multiset<int> int_multiset;
     boost::unordered_multiset<
         test::minimal::assignable,
         test::minimal::hash<test::minimal::assignable>,
         test::minimal::equal_to<test::minimal::assignable>,
         test::minimal::allocator<test::minimal::assignable> > multiset;
 
+ container_test(int_multiset, 0);
     container_test(multiset, assignable);
 }
 
+UNORDERED_AUTO_TEST(equality_tests) {
+ typedef test::minimal::assignable value_type;
+
+ boost::unordered_set<int> int_set;
+
+ boost::unordered_set<
+ test::minimal::assignable,
+ test::minimal::hash<test::minimal::assignable>,
+ test::minimal::equal_to<test::minimal::assignable>,
+ test::minimal::allocator<value_type> > set;
+
+ equality_test(int_set);
+ equality_test(set);
+
+ boost::unordered_multiset<int> int_multiset;
+
+ boost::unordered_multiset<
+ test::minimal::assignable,
+ test::minimal::hash<test::minimal::assignable>,
+ test::minimal::equal_to<test::minimal::assignable>,
+ test::minimal::allocator<value_type> > multiset;
+
+ equality_test(int_multiset);
+ equality_test(multiset);
+}
+
 UNORDERED_AUTO_TEST(test1)
 {
     boost::hash<int> hash;

Modified: branches/unordered/trunk/libs/unordered/test/unordered/compile_tests.hpp
==============================================================================
--- branches/unordered/trunk/libs/unordered/test/unordered/compile_tests.hpp (original)
+++ branches/unordered/trunk/libs/unordered/test/unordered/compile_tests.hpp 2008-04-26 12:23:51 EDT (Sat, 26 Apr 2008)
@@ -28,7 +28,7 @@
 template <class T> T rvalue(T const& v) { return v; }
 
 template <class X, class T>
-void container_test(X& r, T&)
+void container_test(X& r, T const&)
 {
     typedef BOOST_DEDUCED_TYPENAME X::iterator iterator;
     typedef BOOST_DEDUCED_TYPENAME X::const_iterator const_iterator;
@@ -121,8 +121,6 @@
     test::check_return_type<const_iterator>::equals(a.cend());
     test::check_return_type<const_iterator>::equals(a_const.cend());
 
- // No tests for ==, != since they're not required for unordered containers.
-
     a.swap(b);
     test::check_return_type<X>::equals_ref(r = a);
     test::check_return_type<size_type>::equals(a.size());
@@ -161,6 +159,20 @@
 #endif
 }
 
+template <class X>
+void equality_test(X& r)
+{
+ X const a = r, b = r;
+
+ test::check_return_type<bool>::equals(a == b);
+ test::check_return_type<bool>::equals(a != b);
+#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
+ test::check_return_type<std::size_t>::equals(boost::hash_value(a));
+#else
+ test::check_return_type<std::size_t>::equals(hash_value(a));
+#endif
+}
+
 template <class X, class T>
 void unordered_unique_test(X& r, T const& t)
 {

Added: branches/unordered/trunk/libs/unordered/test/unordered/equality_tests.cpp
==============================================================================
--- (empty file)
+++ branches/unordered/trunk/libs/unordered/test/unordered/equality_tests.cpp 2008-04-26 12:23:51 EDT (Sat, 26 Apr 2008)
@@ -0,0 +1,129 @@
+
+// Copyright 2008 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 <boost/unordered_set.hpp>
+#include <boost/unordered_map.hpp>
+#include "../helpers/test.hpp"
+#include "../objects/test.hpp"
+#include "../helpers/random_values.hpp"
+#include <list>
+
+namespace equality_tests {
+ test::seed_t seed(78634);
+
+ template<class T>
+ struct container_holder {
+ test::random_values<T> values;
+ T container;
+
+ container_holder()
+ : values(0), container()
+ {}
+
+ container_holder(int count, test::random_generator const& generator)
+ : values(count, generator),
+ container(values.begin(), values.end())
+ {
+ }
+ };
+
+ template <class T>
+ void equality_tests1(T*, test::random_generator generator
+ = test::default_generator)
+ {
+ const std::size_t num_containers = 100;
+
+ std::list<container_holder<T> > containers;
+ container_holder<T> empty;
+ containers.push_back(container_holder<T>());
+ BOOST_CHECK(empty.values == empty.values);
+ BOOST_CHECK(empty.values == containers.back().values);
+ BOOST_CHECK(empty.container == empty.container);
+ BOOST_CHECK(empty.container == containers.back().container);
+
+ container_holder<T> one(1, generator);
+ containers.push_back(one);
+
+ BOOST_CHECK(empty.values != one.values);
+ BOOST_CHECK(one.values == one.values);
+ BOOST_CHECK(one.values == containers.back().values);
+ BOOST_CHECK(empty.container != one.container);
+ BOOST_CHECK(one.container == one.container);
+ BOOST_CHECK(one.container == containers.back().container);
+
+ container_holder<T> hundred(100, generator);
+ container_holder<T> hundred2(100, generator);
+ BOOST_CHECK(hundred.values != hundred2.values);
+
+ containers.push_back(hundred);
+ containers.push_back(hundred2);
+
+ BOOST_CHECK(empty.values != hundred.values);
+ BOOST_CHECK(one.values != hundred.values);
+ BOOST_CHECK(hundred.values == hundred.values);
+ BOOST_CHECK(hundred2.values != hundred.values);
+ BOOST_CHECK(hundred.values == hundred.values);
+ BOOST_CHECK(hundred2.values == containers.back().values);
+
+ BOOST_CHECK(empty.container != hundred.container);
+ BOOST_CHECK(one.container != hundred.container);
+ BOOST_CHECK(hundred.container == hundred.container);
+ BOOST_CHECK(hundred2.container != hundred.container);
+ BOOST_CHECK(hundred.container == hundred.container);
+ BOOST_CHECK(hundred2.container == containers.back().container);
+
+ for(std::size_t i = containers.size(); i < num_containers; ++i) {
+ using namespace std;
+ containers.push_back(
+ container_holder<T>(rand() % 150, generator));
+ }
+
+ std::size_t count1, count2;
+ typename std::list<container_holder<T> >::const_iterator it1, it2;
+ for(it1 = containers.begin(), count1 = 0; it1 != containers.end(); ++it1, ++count1) {
+ for(it2 = it1, count2 = count1; it2 != containers.end(); ++it2, ++count2) {
+ if(it1 == it2) {
+ if(it1->container != it2->container ||
+ !(it1->container == it2->container))
+ {
+ std::cerr<<"Container "<<count1<<":\n";
+ BOOST_ERROR("Not equal to itself!");
+ }
+ }
+ else if(it1->values == it2->values) {
+ if(it1->container != it2->container ||
+ !(it1->container == it2->container))
+ {
+ std::cerr<<"Containers "<<count1<<","<<count2<<":\n";
+ BOOST_ERROR("Should be equal");
+ }
+ }
+ else {
+ if(it1->container == it2->container ||
+ !(it1->container != it2->container))
+ {
+ std::cerr<<"Containers "<<count1<<","<<count2<<":\n";
+ BOOST_ERROR("Should not be equal");
+ }
+ }
+ }
+ }
+ }
+
+ boost::unordered_set<test::object, test::hash, test::equal_to, test::allocator<test::object> >* test_set;
+ boost::unordered_multiset<test::object, test::hash, test::equal_to, test::allocator<test::object> >* test_multiset;
+ boost::unordered_map<test::object, test::object, test::hash, test::equal_to, test::allocator<test::object> >* test_map;
+ boost::unordered_multimap<test::object, test::object, test::hash, test::equal_to, test::allocator<test::object> >* test_multimap;
+
+ using test::default_generator;
+ using test::generate_collisions;
+
+ UNORDERED_TEST(equality_tests1,
+ ((test_set)(test_multiset)(test_map)(test_multimap))
+ ((default_generator)(generate_collisions))
+ )
+}
+
+RUN_TESTS()


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk