From f962857e6846548524945554c79681bef9560e7b Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 30 Dec 2009 22:17:48 +0000 Subject: [PATCH] Merge unordered. [SVN r58605] --- doc/changes.qbk | 8 + doc/ref.xml | 256 +++++++++++++++++++++ include/boost/unordered/detail/buckets.hpp | 2 +- include/boost/unordered/detail/fwd.hpp | 8 +- include/boost/unordered/detail/table.hpp | 45 +++- include/boost/unordered/unordered_map.hpp | 54 ++++- include/boost/unordered/unordered_set.hpp | 33 ++- test/exception/Jamfile.v2 | 4 +- test/unordered/Jamfile.v2 | 4 +- test/unordered/erase_tests.cpp | 49 ++++ test/unordered/find_tests.cpp | 53 +++++ 11 files changed, 505 insertions(+), 11 deletions(-) diff --git a/doc/changes.qbk b/doc/changes.qbk index d1b26254..fa454d4b 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -107,5 +107,13 @@ First official release. * Support instantiating the containers with incomplete value types. * Reduced the number of warnings (mostly in tests). +* Improved codegear compatibility. +* [@http://svn.boost.org/trac/boost/ticket/3693 Ticket 3693]: + Add `erase_return_void` as a temporary workaround for the current + `erase` which can be inefficient because it has to find the next + element to return an iterator. +* Add templated find overload for compatible keys. +* [@http://svn.boost.org/trac/boost/ticket/3773 Ticket 3773]: + Add missing `std` qualifier to `ptrdiff_t`. [endsect] diff --git a/doc/ref.xml b/doc/ref.xml index 4ca51abf..76d15a1a 100644 --- a/doc/ref.xml +++ b/doc/ref.xml @@ -459,6 +459,15 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Only throws an exception if it is thrown by hasher or key_equal. In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + When the number of elements is a lot smaller than the number of buckets + this function can be very inefficient as it has to search through empty + buckets for the next element, in order to return the iterator. + As a temporary workaround, the container has the method + erase_return_void which will be faster. + + @@ -494,6 +503,27 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + const_iterator + + iterator + + Erase the element pointed to by position. + + + Only throws an exception if it is thrown by hasher or key_equal. + In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + + This is a temporary workaround for the inefficient + erase method. Hopefully, in a future + version the signature of erase will + be changed and this will be deprecated. + + + void @@ -544,8 +574,42 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) key_type const& + const_iterator + + + + + CompatibleKey const& + + + CompatibleHash const& + + + CompatiblePredicate const& + iterator + + + + CompatibleKey const& + + + CompatibleHash const& + + + CompatiblePredicate const& + + const_iterator + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. @@ -1252,6 +1316,15 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Only throws an exception if it is thrown by hasher or key_equal. In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + When the number of elements is a lot smaller than the number of buckets + this function can be very inefficient as it has to search through empty + buckets for the next element, in order to return the iterator. + As a temporary workaround, the container has the method + erase_return_void which will be faster. + + @@ -1287,6 +1360,27 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + const_iterator + + iterator + + Erase the element pointed to by position. + + + Only throws an exception if it is thrown by hasher or key_equal. + In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + + This is a temporary workaround for the inefficient + erase method. Hopefully, in a future + version the signature of erase will + be changed and this will be deprecated. + + + void @@ -1337,8 +1431,42 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) key_type const& + const_iterator + + + + + CompatibleKey const& + + + CompatibleHash const& + + + CompatiblePredicate const& + iterator + + + + CompatibleKey const& + + + CompatibleHash const& + + + CompatiblePredicate const& + + const_iterator + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. @@ -2059,6 +2187,15 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Only throws an exception if it is thrown by hasher or key_equal. In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + When the number of elements is a lot smaller than the number of buckets + this function can be very inefficient as it has to search through empty + buckets for the next element, in order to return the iterator. + As a temporary workaround, the container has the method + erase_return_void which will be faster. + + @@ -2094,6 +2231,27 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + const_iterator + + iterator + + Erase the element pointed to by position. + + + Only throws an exception if it is thrown by hasher or key_equal. + In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + + This is a temporary workaround for the inefficient + erase method. Hopefully, in a future + version the signature of erase will + be changed and this will be deprecated. + + + void @@ -2144,8 +2302,42 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) key_type const& + const_iterator + + + + + CompatibleKey const& + + + CompatibleHash const& + + + CompatiblePredicate const& + iterator + + + + CompatibleKey const& + + + CompatibleHash const& + + + CompatiblePredicate const& + + const_iterator + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. @@ -2901,6 +3093,15 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Only throws an exception if it is thrown by hasher or key_equal. In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + When the number of elements is a lot smaller than the number of buckets + this function can be very inefficient as it has to search through empty + buckets for the next element, in order to return the iterator. + As a temporary workaround, the container has the method + erase_return_void which will be faster. + + @@ -2936,6 +3137,27 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + const_iterator + + iterator + + Erase the element pointed to by position. + + + Only throws an exception if it is thrown by hasher or key_equal. + In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + + This is a temporary workaround for the inefficient + erase method. Hopefully, in a future + version the signature of erase will + be changed and this will be deprecated. + + + void @@ -2986,8 +3208,42 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) key_type const& + const_iterator + + + + + CompatibleKey const& + + + CompatibleHash const& + + + CompatiblePredicate const& + iterator + + + + CompatibleKey const& + + + CompatibleHash const& + + + CompatiblePredicate const& + + const_iterator + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. diff --git a/include/boost/unordered/detail/buckets.hpp b/include/boost/unordered/detail/buckets.hpp index c4b8a898..b1dee5cf 100644 --- a/include/boost/unordered/detail/buckets.hpp +++ b/include/boost/unordered/detail/buckets.hpp @@ -148,7 +148,7 @@ namespace boost { namespace unordered_detail { // Set up the sentinel (node_ptr cast) bucket_ptr sentinel = constructor.get() + - static_cast(this->bucket_count_); + static_cast(this->bucket_count_); sentinel->next_ = sentinel; // Only release the buckets once everything is successfully diff --git a/include/boost/unordered/detail/fwd.hpp b/include/boost/unordered/detail/fwd.hpp index ff3d63e9..ab028710 100644 --- a/include/boost/unordered/detail/fwd.hpp +++ b/include/boost/unordered/detail/fwd.hpp @@ -466,6 +466,9 @@ namespace boost { namespace unordered_detail { return extractor::extract(node::get_value(n)); } bool equal(key_type const& k, value_type const& v) const; + template + node_ptr find_iterator(bucket_ptr bucket, Key const& k, + Pred const&) const; node_ptr find_iterator(bucket_ptr bucket, key_type const& k) const; node_ptr find_iterator(key_type const& k) const; node_ptr* find_for_erase(bucket_ptr bucket, key_type const& k) const; @@ -523,6 +526,8 @@ namespace boost { namespace unordered_detail { std::size_t count(key_type const& k) const; iterator_base find(key_type const& k) const; + template + iterator_base find(Key const& k, Hash const& h, Pred const& eq) const; value_type& at(key_type const& k) const; iterator_pair equal_range(key_type const& k) const; @@ -532,7 +537,8 @@ namespace boost { namespace unordered_detail { void clear(); std::size_t erase_key(key_type const& k); - iterator_base erase(iterator_base r); + iterator_base erase_return_iterator(iterator_base r); + void erase(iterator_base r); std::size_t erase_group(node_ptr* it, bucket_ptr bucket); iterator_base erase_range(iterator_base r1, iterator_base r2); diff --git a/include/boost/unordered/detail/table.hpp b/include/boost/unordered/detail/table.hpp index 2f4e5ebd..198548d3 100644 --- a/include/boost/unordered/detail/table.hpp +++ b/include/boost/unordered/detail/table.hpp @@ -28,6 +28,23 @@ namespace boost { namespace unordered_detail { return this->key_eq()(k, get_key(v)); } + // strong exception safety, no side effects + template + template + inline BOOST_DEDUCED_TYPENAME T::node_ptr + hash_table::find_iterator(bucket_ptr bucket, Key const& k, + Pred const& eq) const + { + node_ptr it = bucket->next_; + while (BOOST_UNORDERED_BORLAND_BOOL(it) && + !eq(k, get_key(node::get_value(it)))) + { + it = node::next_group(it); + } + + return it; + } + // strong exception safety, no side effects template inline BOOST_DEDUCED_TYPENAME T::node_ptr @@ -570,6 +587,22 @@ namespace boost { namespace unordered_detail { return this->end(); } + template + template + BOOST_DEDUCED_TYPENAME T::iterator_base hash_table::find(Key const& k, + Hash const& h, Pred const& eq) const + { + if(!this->size_) return this->end(); + + bucket_ptr bucket = this->get_bucket(h(k) % this->bucket_count_); + node_ptr it = find_iterator(bucket, k, eq); + + if (BOOST_UNORDERED_BORLAND_BOOL(it)) + return iterator_base(bucket, it); + else + return this->end(); + } + template BOOST_DEDUCED_TYPENAME T::value_type& hash_table::at(key_type const& k) const @@ -652,10 +685,20 @@ namespace boost { namespace unordered_detail { return *it ? this->erase_group(it, bucket) : 0; } + template + void hash_table::erase(iterator_base r) + { + BOOST_ASSERT(r.node_); + --this->size_; + node::unlink_node(*r.bucket_, r.node_); + this->delete_node(r.node_); + // r has been invalidated but its bucket is still valid + this->recompute_begin_bucket(r.bucket_); + } template BOOST_DEDUCED_TYPENAME T::iterator_base - hash_table::erase(iterator_base r) + hash_table::erase_return_iterator(iterator_base r) { BOOST_ASSERT(r.node_); iterator_base next = r; diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index ef649897..58ca3014 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -355,7 +355,7 @@ namespace boost iterator erase(const_iterator position) { - return iterator(table_.erase(get(position))); + return iterator(table_.erase_return_iterator(get(position))); } size_type erase(const key_type& k) @@ -368,6 +368,11 @@ namespace boost return iterator(table_.erase_range(get(first), get(last))); } + void erase_return_void(const_iterator position) + { + table_.erase(get(position)); + } + void clear() { table_.clear(); @@ -417,6 +422,26 @@ namespace boost return const_iterator(table_.find(k)); } + template + iterator find( + CompatibleKey const& k, + CompatibleHash const& hash, + CompatiblePredicate const& eq) + { + return iterator(table_.find(k, hash, eq)); + } + + template + const_iterator find( + CompatibleKey const& k, + CompatibleHash const& hash, + CompatiblePredicate const& eq) const + { + return iterator(table_.find(k, hash, eq)); + } + size_type count(const key_type& k) const { return table_.count(k); @@ -868,7 +893,7 @@ namespace boost iterator erase(const_iterator position) { - return iterator(table_.erase(get(position))); + return iterator(table_.erase_return_iterator(get(position))); } size_type erase(const key_type& k) @@ -881,6 +906,11 @@ namespace boost return iterator(table_.erase_range(get(first), get(last))); } + void erase_return_void(const_iterator position) + { + table_.erase(get(position)); + } + void clear() { table_.clear(); @@ -915,6 +945,26 @@ namespace boost return const_iterator(table_.find(k)); } + template + iterator find( + CompatibleKey const& k, + CompatibleHash const& hash, + CompatiblePredicate const& eq) + { + return iterator(table_.find(k, hash, eq)); + } + + template + const_iterator find( + CompatibleKey const& k, + CompatibleHash const& hash, + CompatiblePredicate const& eq) const + { + return iterator(table_.find(k, hash, eq)); + } + size_type count(const key_type& k) const { return table_.count(k); diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 59843fd6..87c1011b 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -348,7 +348,7 @@ namespace boost iterator erase(const_iterator position) { - return iterator(table_.erase(get(position))); + return iterator(table_.erase_return_iterator(get(position))); } size_type erase(const key_type& k) @@ -361,6 +361,11 @@ namespace boost return iterator(table_.erase_range(get(first), get(last))); } + void erase_return_void(const_iterator position) + { + table_.erase(get(position)); + } + void clear() { table_.clear(); @@ -390,6 +395,15 @@ namespace boost return const_iterator(table_.find(k)); } + template + const_iterator find( + CompatibleKey const& k, + CompatibleHash const& hash, + CompatiblePredicate const& eq) const + { + return iterator(table_.find(k, hash, eq)); + } size_type count(const key_type& k) const { return table_.count(k); @@ -822,7 +836,7 @@ namespace boost iterator erase(const_iterator position) { - return iterator(table_.erase(get(position))); + return iterator(table_.erase_return_iterator(get(position))); } size_type erase(const key_type& k) @@ -835,6 +849,11 @@ namespace boost return iterator(table_.erase_range(get(first), get(last))); } + void erase_return_void(const_iterator position) + { + table_.erase(get(position)); + } + void clear() { table_.clear(); @@ -864,6 +883,16 @@ namespace boost return const_iterator(table_.find(k)); } + template + const_iterator find( + CompatibleKey const& k, + CompatibleHash const& hash, + CompatiblePredicate const& eq) const + { + return iterator(table_.find(k, hash, eq)); + } + size_type count(const key_type& k) const { return table_.count(k); diff --git a/test/exception/Jamfile.v2 b/test/exception/Jamfile.v2 index 3fc78437..cc0ec8e8 100644 --- a/test/exception/Jamfile.v2 +++ b/test/exception/Jamfile.v2 @@ -17,8 +17,8 @@ project unordered-test/exception-tests gcc:_GLIBCXX_DEBUG darwin:_GLIBCXX_DEBUG msvc:on - gcc:on - darwin:on + #gcc:on + #darwin:on ; test-suite unordered-exception diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 index 2fdfd357..9454d206 100644 --- a/test/unordered/Jamfile.v2 +++ b/test/unordered/Jamfile.v2 @@ -14,8 +14,8 @@ project unordered-test/unordered gcc:_GLIBCXX_DEBUG darwin:_GLIBCXX_DEBUG msvc:on - gcc:on - darwin:on + #gcc:on + #darwin:on ; test-suite unordered diff --git a/test/unordered/erase_tests.cpp b/test/unordered/erase_tests.cpp index eab22f4c..1bc6e4c6 100644 --- a/test/unordered/erase_tests.cpp +++ b/test/unordered/erase_tests.cpp @@ -112,6 +112,55 @@ void erase_tests1(Container*, test::random_generator generator = test::default_g BOOST_TEST(x.erase(x.begin(), x.end()) == x.begin()); } + std::cerr<<"erase_return_void(begin()).\n"; + { + test::random_values v(1000, generator); + Container x(v.begin(), v.end()); + std::size_t size = x.size(); + while(size > 0 && !x.empty()) + { + BOOST_DEDUCED_TYPENAME Container::key_type key = test::get_key(*x.begin()); + std::size_t count = x.count(key); + x.erase_return_void(x.begin()); + --size; + BOOST_TEST(x.count(key) == count - 1); + BOOST_TEST(x.size() == size); + } + BOOST_TEST(x.empty()); + } + + std::cerr<<"erase_return_void(random position).\n"; + { + test::random_values v(1000, generator); + Container x(v.begin(), v.end()); + std::size_t size = x.size(); + while(size > 0 && !x.empty()) + { + using namespace std; + int index = rand() % (int) x.size(); + BOOST_DEDUCED_TYPENAME Container::const_iterator prev, pos, next; + if(index == 0) { + prev = pos = x.begin(); + } + else { + prev = boost::next(x.begin(), index - 1); + pos = boost::next(prev); + } + next = boost::next(pos); + BOOST_DEDUCED_TYPENAME Container::key_type key = test::get_key(*pos); + std::size_t count = x.count(key); + x.erase_return_void(pos); + --size; + if(size > 0) + BOOST_TEST(index == 0 ? next == x.begin() : + next == boost::next(prev)); + BOOST_TEST(x.count(key) == count - 1); + BOOST_TEST(x.size() == size); + } + BOOST_TEST(x.empty()); + } + + std::cerr<<"clear().\n"; { test::random_values v(500, generator); diff --git a/test/unordered/find_tests.cpp b/test/unordered/find_tests.cpp index 24523fd3..e823a04d 100644 --- a/test/unordered/find_tests.cpp +++ b/test/unordered/find_tests.cpp @@ -83,6 +83,55 @@ void find_tests1(X*, test::random_generator generator = test::default_generator) } } +struct compatible_key +{ + test::object o_; + + compatible_key(test::object const& o) : o_(o) {} +}; + +struct compatible_hash +{ + test::hash hash_; + + std::size_t operator()(compatible_key const& k) const { + return hash_(k.o_); + } +}; + +struct compatible_predicate +{ + test::equal_to equal_; + + bool operator()(compatible_key const& k1, compatible_key const& k2) const { + return equal_(k1.o_, k2.o_); + } +}; + +template +void find_compatible_keys_test(X*, test::random_generator generator = test::default_generator) +{ + typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; + typedef BOOST_DEDUCED_TYPENAME test::random_values::iterator value_iterator; + test::random_values v(500, generator); + X x(v.begin(), v.end()); + + compatible_hash h; + compatible_predicate eq; + + for(value_iterator it = v.begin(), end = v.end(); it != end; ++it) { + BOOST_DEDUCED_TYPENAME X::key_type key = test::get_key(*it); + BOOST_TEST(x.find(key) == x.find(compatible_key(key), h, eq)); + } + + test::random_values v2(20, generator); + + for(value_iterator it = v2.begin(), end = v2.end(); it != end; ++it) { + BOOST_DEDUCED_TYPENAME X::key_type key = test::get_key(*it); + BOOST_TEST(x.find(key) == x.find(compatible_key(key), h, eq)); + } +} + boost::unordered_set >* test_set; boost::unordered_multiset >* test_multiset; boost::unordered_map >* test_map; @@ -95,6 +144,10 @@ UNORDERED_TEST(find_tests1, ((test_set)(test_multiset)(test_map)(test_multimap)) ((default_generator)(generate_collisions)) ) +UNORDERED_TEST(find_compatible_keys_test, + ((test_set)(test_multiset)(test_map)(test_multimap)) + ((default_generator)(generate_collisions)) +) }