From ef2f5116dd6b1291a3e6201d3d3a83c26e7cb358 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 20 May 2007 17:37:27 +0000 Subject: [PATCH] It is currently proposed that insert, erase and rehash should be stable. Change insert(hint, value) so that it inserts at the end of a group of equivalent keys (all the other functions were already stable). [SVN r4146] --- doc/rationale.qbk | 7 +- .../unordered/detail/hash_table_impl.hpp | 9 ++- test/unordered/Jamfile.v2 | 1 + test/unordered/insert_stable_tests.cpp | 74 +++++++++++++++++++ 4 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 test/unordered/insert_stable_tests.cpp diff --git a/doc/rationale.qbk b/doc/rationale.qbk index dbc6e089..a688c18c 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -118,12 +118,7 @@ should probably change it to a slow swap. [h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#518 518. Are insert and erase stable for unordered_multiset and unordered_multimap?]] -In this implementation, erase is stable. All inserts are stable, except for -inserting with a hint, which has slightly surprising behaviour. If the hint -points to the first element in the correct equal range it inserts at the end of -the range, for all other elements in the range it inserts immediately before -the element. I am very tempted to change insert with a hint to just ignore the -hint completely. +The current proposal is that insert and erase are stable - so they are here. [h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#528 528. TR1: issue 6.19 vs 6.3.4.3/2 (and 6.3.4.5/2)]] diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index 9350719c..1bca2414 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -1561,6 +1561,13 @@ namespace boost { return insert(v); } else { + // Find the first node in the group - so that the node + // will be inserted at the end of the group. + + local_iterator_base start(it.local_); + while(prev_in_group(start.node_)->next_ == start.node_) + start.node_ = prev_in_group(start.node_); + // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). node_constructor a(this->allocators_); @@ -1574,7 +1581,7 @@ namespace boost { // Nothing after this point can throw link_ptr n = a.release(); - this->link_node(n, it.local_); + this->link_node(n, start); return iterator_base(base, n); } diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 index c4ce121c..07186630 100644 --- a/test/unordered/Jamfile.v2 +++ b/test/unordered/Jamfile.v2 @@ -18,6 +18,7 @@ test-suite unordered-tests [ run copy_tests.cpp ] [ run assign_tests.cpp ] [ run insert_tests.cpp ] + [ run insert_stable_tests.cpp ] [ run unnecessary_copy_tests.cpp ] [ run erase_tests.cpp ] [ run erase_equiv_tests.cpp ] diff --git a/test/unordered/insert_stable_tests.cpp b/test/unordered/insert_stable_tests.cpp new file mode 100644 index 00000000..51b16e78 --- /dev/null +++ b/test/unordered/insert_stable_tests.cpp @@ -0,0 +1,74 @@ + +// Copyright 2007 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 +#include +#include + +#include + +struct member { + int tag1_; + int tag2_; + + member() : tag1_(0), tag2_(0) {} + member(int t1, int t2) : tag1_(t1), tag2_(t2) {} + + friend bool operator==(member const& x, member const& y) { + return x.tag1_ == y.tag1_; + } + + friend bool operator!=(member const& x, member const& y) { + return x.tag1_ != y.tag1_; + } +}; + +std::size_t hash_value(member const& x) { + return static_cast(x.tag1_); +} + +void stable_insert_test1() { + boost::unordered_multiset x; + + x.insert(member(1,1)); + x.insert(member(1,2)); + x.insert(member(1,3)); + + boost::unordered_multiset::const_iterator it = x.begin(), end = x.end(); + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->tag2_ == 1); ++it; } + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->tag2_ == 2); ++it; } + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->tag2_ == 3); ++it; } + BOOST_TEST(it == end); +} + +void stable_insert_test2() { + boost::unordered_multimap x; + typedef boost::unordered_multimap::const_iterator iterator; + + iterator it = x.insert(x.end(), std::make_pair(member(1,1), 1)); + it = x.insert(it, std::make_pair(member(1,2), 2)); + it = x.insert(it, std::make_pair(member(1,3), 3)); + + it = x.begin(); + iterator end = x.end(); + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->first.tag2_ == 1 && it->second == 1); ++it; } + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->first.tag2_ == 2 && it->second == 2); ++it; } + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->first.tag2_ == 3 && it->second == 3); ++it; } + BOOST_TEST(it == end); +} + +int main() +{ + stable_insert_test1(); + stable_insert_test2(); + + return boost::report_errors(); +}