From e911a8011b960098208606d2c93d391eb47329e3 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 25 May 2009 19:57:04 +0000 Subject: [PATCH] Merge unordered changes: * Support emplace for all compilers. * Better configuration of C++0x features for when the appropriate headers aren't available. Merged revisions 52393-52394,52397,52884-52885,53127,53255 via svnmerge from https://svn.boost.org/svn/boost/trunk ........ r52393 | danieljames | 2009-04-14 18:23:37 +0100 (Tue, 14 Apr 2009) | 2 lines Implement full extract_key for compilers without SFINAE and variadic templates. ........ r52394 | danieljames | 2009-04-14 18:23:51 +0100 (Tue, 14 Apr 2009) | 1 line Use emplace instead of insert in the backend as it's more appropriate. ........ r52397 | danieljames | 2009-04-14 18:51:34 +0100 (Tue, 14 Apr 2009) | 1 line Add stream output to the count test helper for unordered. ........ r52884 | danieljames | 2009-05-10 22:24:41 +0100 (Sun, 10 May 2009) | 19 lines Cherrypick some unordered container changes from sandbox. Not including anything which depends on the new move library. ------------------------------------------------------------------------ r52746 | danieljames | 2009-05-03 11:12:30 +0100 (Sun, 03 May 2009) | 1 line Merge latest unordered container changes. ------------------------------------------------------------------------ r52747 | danieljames | 2009-05-03 11:15:35 +0100 (Sun, 03 May 2009) | 4 lines Put the C++0x emplace implementations before the non-C++0x versions. I'm going to change the non-C++0x to be macro heavy emulations of the C++0x versions, so this will put the readable version first. ------------------------------------------------------------------------ r52748 | danieljames | 2009-05-03 11:15:44 +0100 (Sun, 03 May 2009) | 1 line Refactor the unordered implementation a tad, to make implementing emplace less painful. ------------------------------------------------------------------------ ........ r52885 | danieljames | 2009-05-10 22:25:09 +0100 (Sun, 10 May 2009) | 1 line Merge emplace support for sandbox - but without move support. ........ r53127 | danieljames | 2009-05-20 07:43:38 +0100 (Wed, 20 May 2009) | 1 line Better configuration for boost.unordered. ........ r53255 | danieljames | 2009-05-25 20:45:06 +0100 (Mon, 25 May 2009) | 1 line Unordered change log. ........ [SVN r53257] --- doc/changes.qbk | 10 + include/boost/unordered/detail/config.hpp | 8 + include/boost/unordered/detail/hash_table.hpp | 27 +- .../unordered/detail/hash_table_impl.hpp | 603 ++++++++++++------ include/boost/unordered/unordered_map.hpp | 116 +++- include/boost/unordered/unordered_set.hpp | 115 +++- test/helpers/count.hpp | 7 + test/objects/exception.hpp | 2 +- test/objects/minimal.hpp | 2 +- test/objects/test.hpp | 2 +- test/unordered/compile_tests.hpp | 8 - test/unordered/unnecessary_copy_tests.cpp | 64 +- 12 files changed, 718 insertions(+), 246 deletions(-) diff --git a/doc/changes.qbk b/doc/changes.qbk index 396eb887..a8b81eef 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -69,5 +69,15 @@ First official release. * Some other minor internal changes to the implementation, tests and documentation. * Avoid an unnecessary copy in `operator[]`. +* [@https://svn.boost.org/trac/boost/ticket/2975 Ticket 2975]: Fix length of + prime number list. + +[h2 Boost 1.40.0] + +* [@https://svn.boost.org/trac/boost/ticket/2975 Ticket 2975]: + Store the prime list as a preprocessor sequence - so that it will always get + the length right if it changes again in the future. +* [@https://svn.boost.org/trac/boost/ticket/1978 Ticket 1978]: + Implement `emplace` for all compilers. [endsect] diff --git a/include/boost/unordered/detail/config.hpp b/include/boost/unordered/detail/config.hpp index f277feae..68c9875a 100644 --- a/include/boost/unordered/detail/config.hpp +++ b/include/boost/unordered/detail/config.hpp @@ -19,4 +19,12 @@ # define BOOST_UNORDERED_NO_HAS_MOVE_ASSIGN #endif +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) +# if defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) + // STLport doesn't have std::forward. +# else +# define BOOST_UNORDERED_STD_FORWARD +# endif +#endif + #endif diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index f64e3e83..8a458f7f 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -12,6 +12,11 @@ #endif #include +#include + +#if !defined(BOOST_UNORDERED_EMPLACE_LIMIT) +#define BOOST_UNORDERED_EMPLACE_LIMIT 5 +#endif #include #include @@ -28,8 +33,12 @@ #include #include #include +#include +#include #include #include +#include +#include #include #include #include @@ -37,11 +46,19 @@ #include -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) -#include -#include -#include -#include +#if !(defined(BOOST_UNORDERED_STD_FORWARD)) + +#include +#include +#include + +#define BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + BOOST_PP_ENUM_PARAMS_Z(z, n, typename Arg) +#define BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, Arg, const& arg) +#define BOOST_UNORDERED_CALL_PARAMS(z, n) \ + BOOST_PP_ENUM_PARAMS_Z(z, n, arg) + #endif #if BOOST_WORKAROUND(__BORLANDC__, <= 0x0582) diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index e7cdc5b9..bc842dc8 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -181,56 +181,159 @@ namespace boost { } } -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + void construct_preamble() + { + if(!node_) { + node_constructed_ = false; + value_constructed_ = false; + + node_ = allocators_.node_alloc_.allocate(1); + allocators_.node_alloc_.construct(node_, node()); + node_constructed_ = true; + } + else { + BOOST_ASSERT(node_constructed_ && value_constructed_); + BOOST_UNORDERED_DESTRUCT(&node_->value(), value_type); + value_constructed_ = false; + } + } + +#if defined(BOOST_UNORDERED_STD_FORWARD) template void construct(Args&&... args) { - BOOST_ASSERT(!node_); - node_constructed_ = false; - value_constructed_ = false; - - node_ = allocators_.node_alloc_.allocate(1); - - allocators_.node_alloc_.construct(node_, node()); - node_constructed_ = true; - + construct_preamble(); new(node_->address()) value_type(std::forward(args)...); value_constructed_ = true; } -#else - template - void construct(V const& v) + +#if defined(__GLIBCPP__) || defined(__GLIBCXX__) + // The GCC C++0x standard library implementation does not have + // a single argument pair constructor, so this works around that. + + template + void construct(Arg&& arg) { - BOOST_ASSERT(!node_); - node_constructed_ = false; - value_constructed_ = false; - - node_ = allocators_.node_alloc_.allocate(1); - - allocators_.node_alloc_.construct(node_, node()); - node_constructed_ = true; - - new(node_->address()) value_type(v); + construct_preamble(); + construct_impl(std::forward(arg), + (value_type const*) 0, + (typename boost::remove_reference::type const*) 0); value_constructed_ = true; } + + template < + typename Arg, + typename ValueType, + typename Type> + void construct_impl(Arg&& arg, ValueType const*, Type const*) + { + new(node_->address()) value_type(std::forward(arg)); + } + + template < + typename Arg, + typename ValueFirst, typename ValueSecond, + typename TypeFirst, typename TypeSecond> + void construct_impl( + Arg&& arg, + std::pair const*, + std::pair const*) + { + new(node_->address()) value_type(std::forward(arg)); + } + + template < + typename Arg, + typename ValueFirst, typename ValueSecond, + typename Type> + void construct_impl( + Arg&& arg, + std::pair const*, + Type const*) + { + new(node_->address()) value_type(std::forward(arg), ValueSecond()); + } #endif + +#else - template - void construct_pair(K const& k, M*) + void construct() { - BOOST_ASSERT(!node_); - node_constructed_ = false; - value_constructed_ = false; - - node_ = allocators_.node_alloc_.allocate(1); - - allocators_.node_alloc_.construct(node_, node()); - node_constructed_ = true; - - new(node_->address()) value_type(k, M()); + construct_preamble(); + new(node_->address()) value_type; value_constructed_ = true; } +#define BOOST_UNORDERED_CONSTRUCT_IMPL(z, n, _) \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + void construct( \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + construct_preamble(); \ + construct_impl( \ + (value_type*) 0, \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + ); \ + value_constructed_ = true; \ + } \ + \ + template < \ + typename T, \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + void construct_impl( \ + T*, \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + new(node_->address()) value_type( \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + ); \ + } \ + \ + +#define BOOST_UNORDERED_CONSTRUCT_IMPL2(z, n, _) \ + template \ + void construct_impl( \ + std::pair*, \ + Key const& k, \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + new(node_->address()) value_type(k, \ + Second( \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + ) \ + ); \ + } + + BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT, + BOOST_UNORDERED_CONSTRUCT_IMPL, _) + BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT, + BOOST_UNORDERED_CONSTRUCT_IMPL2, _) + + template + void construct_impl(std::pair*, + std::pair const& arg0) + { + new(node_->address()) value_type(arg0); + } + + template + void construct_impl(std::pair*, Key const& k) + { + new(node_->address()) value_type(First(k), Second()); + } + +#undef BOOST_UNORDERED_CONSTRUCT_IMPL + +#endif + node_ptr get() const { BOOST_ASSERT(node_); @@ -1427,8 +1530,29 @@ namespace boost { } // key extractors - + // // no throw + // + // 'extract_key' is called with the emplace parameters to return a + // key if available or 'no_key' is one isn't and will need to be + // constructed. + + struct no_key { + no_key() {} + template no_key(T const&) {} + }; + + + // If emplace is called with no arguments then there obviously + // isn't an available key. + + static no_key extract_key() + { + return no_key(); + } + + // Emplace or insert was called with the value type. + static key_type const& extract_key(value_type const& v) { return extract(v, (type_wrapper*)0); @@ -1445,40 +1569,67 @@ namespace boost { { return v.first; } - -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) - struct no_key {}; - - template - static typename boost::enable_if< - boost::mpl::and_< - boost::mpl::not_ >, - boost::is_same - >, - key_type>::type const& extract_key(Arg1 const& k, Args const&...) + + // For maps, if emplace is called with just a key, then it's the value type + // with the second value default initialised. + + template + static BOOST_DEDUCED_TYPENAME + boost::mpl::if_, key_type const&, no_key>::type + extract_key(Arg const& k) { return k; } + // For a map, the argument might be a pair with the key as the first + // part and a convertible value as the second part. + template - static typename boost::enable_if< - boost::mpl::and_< - boost::mpl::not_ >, - boost::is_same::type - >::type> - >, - key_type>::type const& extract_key(std::pair const& v) + static BOOST_DEDUCED_TYPENAME + boost::mpl::if_< + boost::mpl::and_< + boost::mpl::not_ >, + boost::is_same::type + >::type> + >, + key_type const&, no_key + >::type extract_key(std::pair const& v) { return v.first; } - template - static no_key extract_key(Args const&...) + // For maps if there is more than one argument, the key can be the first argument. + +#if defined(BOOST_UNORDERED_STD_FORWARD) + template + static BOOST_DEDUCED_TYPENAME + boost::mpl::if_< + boost::mpl::and_< + boost::mpl::not_ >, + boost::is_same + >, + key_type const&, no_key + >::type extract_key(Arg const& k, Arg1 const&, Args const&...) { - return no_key(); + return k; } + +#else + template + static BOOST_DEDUCED_TYPENAME + boost::mpl::if_< + boost::mpl::and_< + boost::mpl::not_ >, + boost::is_same + >, + key_type const&, no_key + >::type extract_key(Arg const& k, Arg1 const&) + { + return k; + } + #endif public: @@ -1582,72 +1733,78 @@ namespace boost { #if BOOST_UNORDERED_EQUIVALENT_KEYS -#if !(defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)) - // Insert (equivalent key containers) +#if defined(BOOST_UNORDERED_STD_FORWARD) + + // Emplace (equivalent key containers) + // (I'm using an overloaded emplace for both 'insert' and 'emplace') // if hash function throws, basic exception safety // strong otherwise - iterator_base insert(value_type const& v) + template + iterator_base emplace(Args&&... args) { // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). node_constructor a(data_.allocators_); - a.construct(v); + a.construct(std::forward(args)...); - return insert_impl(a); + return emplace_impl(a); } - // Insert (equivalent key containers) + // Emplace (equivalent key containers) + // (I'm using an overloaded emplace for both 'insert' and 'emplace') // if hash function throws, basic exception safety // strong otherwise - iterator_base insert_hint(iterator_base const& it, value_type const& v) + template + iterator_base emplace_hint(iterator_base const& it, Args&&... args) { // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). node_constructor a(data_.allocators_); - a.construct(v); + a.construct(std::forward(args)...); - return insert_hint_impl(it, a); + return emplace_hint_impl(it, a); } #else - // Insert (equivalent key containers) - // (I'm using an overloaded insert for both 'insert' and 'emplace') - - // if hash function throws, basic exception safety - // strong otherwise - template - iterator_base insert(Args&&... args) - { - // Create the node before rehashing in case it throws an - // exception (need strong safety in such a case). - node_constructor a(data_.allocators_); - a.construct(std::forward(args)...); - - return insert_impl(a); +#define BOOST_UNORDERED_INSERT_IMPL(z, n, _) \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + iterator_base emplace( \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + node_constructor a(data_.allocators_); \ + a.construct( \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + ); \ + return emplace_impl(a); \ + } \ + \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + iterator_base emplace_hint(iterator_base const& it, \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + node_constructor a(data_.allocators_); \ + a.construct( \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + ); \ + return emplace_hint_impl(it, a); \ } - // Insert (equivalent key containers) - // (I'm using an overloaded insert for both 'insert' and 'emplace') - - // if hash function throws, basic exception safety - // strong otherwise - template - iterator_base insert_hint(iterator_base const& it, Args&&... args) - { - // Create the node before rehashing in case it throws an - // exception (need strong safety in such a case). - node_constructor a(data_.allocators_); - a.construct(std::forward(args)...); - - return insert_hint_impl(it, a); - } + BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT, + BOOST_UNORDERED_INSERT_IMPL, _) +#undef BOOST_UNORDERED_INSERT_IMPL #endif - iterator_base insert_impl(node_constructor& a) + iterator_base emplace_impl(node_constructor& a) { key_type const& k = extract_key(a.get()->value()); size_type hash_value = hash_function()(k); @@ -1668,17 +1825,17 @@ namespace boost { ); } - iterator_base insert_hint_impl(iterator_base const& it, node_constructor& a) + iterator_base emplace_hint_impl(iterator_base const& it, node_constructor& a) { // equal can throw, but with no effects if (it == data_.end() || !equal(extract_key(a.get()->value()), *it)) { - // Use the standard insert if the iterator doesn't point + // Use the standard emplace if the iterator doesn't point // to a matching key. - return insert_impl(a); + return emplace_impl(a); } else { // Find the first node in the group - so that the node - // will be inserted at the end of the group. + // will be added at the end of the group. link_ptr start(it.node_); while(data_.prev_in_group(start)->next_ == start) @@ -1707,7 +1864,7 @@ namespace boost { { size_type distance = unordered_detail::distance(i, j); if(distance == 1) { - insert(*i); + emplace(*i); } else { // Only require basic exception safety here @@ -1737,7 +1894,7 @@ namespace boost { { // If only inserting 1 element, get the required // safety since insert is only called once. - for (; i != j; ++i) insert(*i); + for (; i != j; ++i) emplace(*i); } public: @@ -1773,7 +1930,7 @@ namespace boost { // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). node_constructor a(data_.allocators_); - a.construct_pair(k, (mapped_type*) 0); + a.construct(k); // reserve has basic exception safety if the hash function // throws, strong otherwise. @@ -1786,81 +1943,37 @@ namespace boost { } } -#if !(defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL)) +#if defined(BOOST_UNORDERED_STD_FORWARD) - // Insert (unique keys) - - // if hash function throws, basic exception safety - // strong otherwise - std::pair insert(value_type const& v) - { - // No side effects in this initial code - key_type const& k = extract_key(v); - size_type hash_value = hash_function()(k); - bucket_ptr bucket = data_.bucket_ptr_from_hash(hash_value); - link_ptr pos = find_iterator(bucket, k); - - if (BOOST_UNORDERED_BORLAND_BOOL(pos)) { - // Found an existing key, return it (no throw). - return std::pair( - iterator_base(bucket, pos), false); - - } else { - // Doesn't already exist, add to bucket. - // Side effects only in this block. - - // Create the node before rehashing in case it throws an - // exception (need strong safety in such a case). - node_constructor a(data_.allocators_); - a.construct(v); - - // reserve has basic exception safety if the hash function - // throws, strong otherwise. - if(reserve_for_insert(size() + 1)) - bucket = data_.bucket_ptr_from_hash(hash_value); - - // Nothing after this point can throw. - - link_ptr n = data_.link_node_in_bucket(a, bucket); - - return std::pair( - iterator_base(bucket, n), true); - } - } - - // Insert (unique keys) - - // if hash function throws, basic exception safety - // strong otherwise - iterator_base insert_hint(iterator_base const& it, value_type const& v) - { - if(it != data_.end() && equal(extract_key(v), *it)) - return it; - else - return insert(v).first; - } - -#else - - // Insert (unique keys) - // (I'm using an overloaded insert for both 'insert' and 'emplace') - // - // TODO: - // For sets: create a local key without creating the node? - // For maps: use the first argument as the key. + // Emplace (unique keys) + // (I'm using an overloaded emplace for both 'insert' and 'emplace') // if hash function throws, basic exception safety // strong otherwise template - std::pair insert(Args&&... args) + std::pair emplace(Args&&... args) { - return insert_impl( + return emplace_impl( extract_key(std::forward(args)...), std::forward(args)...); } + // Insert (unique keys) + // (I'm using an overloaded emplace for both 'insert' and 'emplace') + // I'm just ignoring hints here for now. + + // if hash function throws, basic exception safety + // strong otherwise template - std::pair insert_impl(key_type const& k, Args&&... args) + iterator_base emplace_hint(iterator_base const&, Args&&... args) + { + return emplace_impl( + extract_key(std::forward(args)...), + std::forward(args)...).first; + } + + template + std::pair emplace_impl(key_type const& k, Args&&... args) { // No side effects in this initial code size_type hash_value = hash_function()(k); @@ -1894,13 +2007,110 @@ namespace boost { } template - std::pair insert_impl(no_key, Args&&... args) + std::pair emplace_impl(no_key, Args&&... args) { // Construct the node regardless - in order to get the key. // It will be discarded if it isn't used node_constructor a(data_.allocators_); a.construct(std::forward(args)...); + return emplace_impl_with_node(a); + } +#else + template + std::pair emplace(Arg0 const& arg0) + { + return emplace_impl(extract_key(arg0), arg0); + } + template + iterator_base emplace_hint(iterator_base const& it, Arg0 const& arg0) + { + return emplace_impl(extract_key(arg0), arg0).first; + } + + +#define BOOST_UNORDERED_INSERT_IMPL(z, n, _) \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + std::pair emplace( \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + return emplace_impl( \ + extract_key(arg0, arg1), \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + ); \ + } \ + \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + iterator_base emplace_hint(iterator_base const& it, \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + return emplace_impl( \ + extract_key(arg0, arg1), \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + ).first; \ + } \ + BOOST_UNORDERED_INSERT_IMPL2(z, n, _) + +#define BOOST_UNORDERED_INSERT_IMPL2(z, n, _) \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + std::pair emplace_impl(key_type const& k, \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + size_type hash_value = hash_function()(k); \ + bucket_ptr bucket = data_.bucket_ptr_from_hash(hash_value); \ + link_ptr pos = find_iterator(bucket, k); \ + \ + if (BOOST_UNORDERED_BORLAND_BOOL(pos)) { \ + return std::pair( \ + iterator_base(bucket, pos), false); \ + } else { \ + node_constructor a(data_.allocators_); \ + a.construct( \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + ); \ + \ + if(reserve_for_insert(size() + 1)) \ + bucket = data_.bucket_ptr_from_hash(hash_value); \ + \ + return std::pair(iterator_base(bucket, \ + data_.link_node_in_bucket(a, bucket)), true); \ + } \ + } \ + \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + std::pair emplace_impl(no_key, \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + node_constructor a(data_.allocators_); \ + a.construct( \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + ); \ + return emplace_impl_with_node(a); \ + } + + BOOST_UNORDERED_INSERT_IMPL2(1, 1, _) + + BOOST_PP_REPEAT_FROM_TO(2, BOOST_UNORDERED_EMPLACE_LIMIT, + BOOST_UNORDERED_INSERT_IMPL, _) + +#undef BOOST_UNORDERED_INSERT_IMPL + +#endif + + std::pair emplace_impl_with_node(node_constructor& a) + { // No side effects in this initial code key_type const& k = extract_key(a.get()->value()); size_type hash_value = hash_function()(k); @@ -1924,19 +2134,6 @@ namespace boost { } } - // Insert (unique keys) - // (I'm using an overloaded insert for both 'insert' and 'emplace') - - // if hash function throws, basic exception safety - // strong otherwise - template - iterator_base insert_hint(iterator_base const&, Args&&... args) - { - // Life is complicated - just call the normal implementation. - return insert(std::forward(args)...).first; - } -#endif - // Insert from iterators (unique keys) template @@ -1963,6 +2160,13 @@ namespace boost { // strong otherwise template void insert_range(InputIterator i, InputIterator j) + { + if(i != j) + return insert_range_impl(extract_key(*i), i, j); + } + + template + void insert_range_impl(key_type const&, InputIterator i, InputIterator j) { node_constructor a(data_.allocators_); @@ -1992,6 +2196,36 @@ namespace boost { } } } + + template + void insert_range_impl(no_key, InputIterator i, InputIterator j) + { + node_constructor a(data_.allocators_); + + for (; i != j; ++i) { + // No side effects in this initial code + a.construct(*i); + key_type const& k = extract_key(a.get()->value()); + size_type hash_value = hash_function()(extract_key(k)); + bucket_ptr bucket = data_.bucket_ptr_from_hash(hash_value); + link_ptr pos = find_iterator(bucket, k); + + if (!BOOST_UNORDERED_BORLAND_BOOL(pos)) { + // Doesn't already exist, add to bucket. + // Side effects only in this block. + + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + if(size() + 1 >= max_load_) { + reserve_for_insert(size() + insert_size(i, j)); + bucket = data_.bucket_ptr_from_hash(hash_value); + } + + // Nothing after this point can throw. + data_.link_node_in_bucket(a, bucket); + } + } + } #endif public: @@ -2078,8 +2312,9 @@ namespace boost { key_type const& k) const { link_ptr it = data_.begin(bucket); - while (BOOST_UNORDERED_BORLAND_BOOL(it) && !equal(k, data::get_value(it))) + while (BOOST_UNORDERED_BORLAND_BOOL(it) && !equal(k, data::get_value(it))) { it = data::next_group(it); + } return it; } diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index f787de2a..5a282879 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -21,6 +21,10 @@ #include #endif +#if !defined(BOOST_NO_0X_HDR_INITIALIZER_LIST) +#include +#endif + #if defined(BOOST_MSVC) #pragma warning(push) #if BOOST_MSVC >= 1400 @@ -135,7 +139,7 @@ namespace boost #endif #endif -#if !defined(BOOST_NO_INITIALIZER_LISTS) +#if !defined(BOOST_NO_0X_HDR_INITIALIZER_LIST) unordered_map(std::initializer_list list, size_type n = boost::unordered_detail::default_initial_bucket_count, const hasher &hf = hasher(), @@ -219,30 +223,74 @@ namespace boost // modifiers -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) +#if defined(BOOST_UNORDERED_STD_FORWARD) template std::pair emplace(Args&&... args) { return boost::unordered_detail::pair_cast( - base.insert(std::forward(args)...)); + base.emplace(std::forward(args)...)); } template iterator emplace_hint(const_iterator hint, Args&&... args) { - return iterator(base.insert_hint(get(hint), std::forward(args)...)); + return iterator(base.emplace_hint(get(hint), std::forward(args)...)); } +#else + + std::pair emplace(value_type const& v = value_type()) + { + return boost::unordered_detail::pair_cast( + base.emplace(v)); + } + + iterator emplace_hint(const_iterator hint, value_type const& v = value_type()) + { + return iterator(base.emplace_hint(get(hint), v)); + } + +#define BOOST_UNORDERED_EMPLACE(z, n, _) \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + std::pair emplace( \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + return boost::unordered_detail::pair_cast( \ + base.emplace( \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + )); \ + } \ + \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + iterator emplace_hint(const_iterator hint, \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + return iterator(base.emplace_hint(get(hint), \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + )); \ + } + + BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT, + BOOST_UNORDERED_EMPLACE, _) + +#undef BOOST_UNORDERED_EMPLACE + #endif std::pair insert(const value_type& obj) { return boost::unordered_detail::pair_cast( - base.insert(obj)); + base.emplace(obj)); } iterator insert(const_iterator hint, const value_type& obj) { - return iterator(base.insert_hint(get(hint), obj)); + return iterator(base.emplace_hint(get(hint), obj)); } template @@ -541,7 +589,7 @@ namespace boost #endif #endif -#if !defined(BOOST_NO_INITIALIZER_LISTS) +#if !defined(BOOST_NO_0X_HDR_INITIALIZER_LIST) unordered_multimap(std::initializer_list list, size_type n = boost::unordered_detail::default_initial_bucket_count, const hasher &hf = hasher(), @@ -626,28 +674,72 @@ namespace boost // modifiers -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) +#if defined(BOOST_UNORDERED_STD_FORWARD) template iterator emplace(Args&&... args) { - return iterator(base.insert(std::forward(args)...)); + return iterator(base.emplace(std::forward(args)...)); } template iterator emplace_hint(const_iterator hint, Args&&... args) { - return iterator(base.insert_hint(get(hint), std::forward(args)...)); + return iterator(base.emplace_hint(get(hint), std::forward(args)...)); } +#else + + iterator emplace(value_type const& v = value_type()) + { + return iterator(base.emplace(v)); + } + + iterator emplace_hint(const_iterator hint, value_type const& v = value_type()) + { + return iterator(base.emplace_hint(get(hint), v)); + } + + +#define BOOST_UNORDERED_EMPLACE(z, n, _) \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + iterator emplace( \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + return iterator( \ + base.emplace( \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + )); \ + } \ + \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + iterator emplace_hint(const_iterator hint, \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + return iterator(base.emplace_hint(get(hint), \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + )); \ + } + + BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT, + BOOST_UNORDERED_EMPLACE, _) + +#undef BOOST_UNORDERED_EMPLACE + #endif iterator insert(const value_type& obj) { - return iterator(base.insert(obj)); + return iterator(base.emplace(obj)); } iterator insert(const_iterator hint, const value_type& obj) { - return iterator(base.insert_hint(get(hint), obj)); + return iterator(base.emplace_hint(get(hint), obj)); } template diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 4e9f39bb..cfe6c9b7 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -21,6 +21,10 @@ #include #endif +#if !defined(BOOST_NO_0X_HDR_INITIALIZER_LIST) +#include +#endif + #if defined(BOOST_MSVC) #pragma warning(push) #if BOOST_MSVC >= 1400 @@ -133,7 +137,7 @@ namespace boost #endif #endif -#if !defined(BOOST_NO_INITIALIZER_LISTS) +#if !defined(BOOST_NO_0X_HDR_INITIALIZER_LIST) unordered_set(std::initializer_list list, size_type n = boost::unordered_detail::default_initial_bucket_count, const hasher &hf = hasher(), @@ -217,31 +221,75 @@ namespace boost // modifiers -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) +#if defined(BOOST_UNORDERED_STD_FORWARD) template std::pair emplace(Args&&... args) { return boost::unordered_detail::pair_cast( - base.insert(std::forward(args)...)); + base.emplace(std::forward(args)...)); } template iterator emplace_hint(const_iterator hint, Args&&... args) { return iterator( - base.insert_hint(get(hint), std::forward(args)...)); + base.emplace_hint(get(hint), std::forward(args)...)); } +#else + + std::pair emplace(value_type const& v = value_type()) + { + return boost::unordered_detail::pair_cast( + base.emplace(v)); + } + + iterator emplace_hint(const_iterator hint, value_type const& v = value_type()) + { + return iterator(base.emplace_hint(get(hint), v)); + } + +#define BOOST_UNORDERED_EMPLACE(z, n, _) \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + std::pair emplace( \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + return boost::unordered_detail::pair_cast( \ + base.emplace( \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + )); \ + } \ + \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + iterator emplace_hint(const_iterator hint, \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + return iterator(base.emplace_hint(get(hint), \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + )); \ + } + + BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT, + BOOST_UNORDERED_EMPLACE, _) + +#undef BOOST_UNORDERED_EMPLACE + #endif std::pair insert(const value_type& obj) { return boost::unordered_detail::pair_cast( - base.insert(obj)); + base.emplace(obj)); } iterator insert(const_iterator hint, const value_type& obj) { - return iterator(base.insert_hint(get(hint), obj)); + return iterator(base.emplace_hint(get(hint), obj)); } template @@ -511,7 +559,7 @@ namespace boost #endif #endif -#if !defined(BOOST_NO_INITIALIZER_LISTS) +#if !defined(BOOST_NO_0X_HDR_INITIALIZER_LIST) unordered_multiset(std::initializer_list list, size_type n = boost::unordered_detail::default_initial_bucket_count, const hasher &hf = hasher(), @@ -595,28 +643,71 @@ namespace boost // modifiers -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) +#if defined(BOOST_UNORDERED_STD_FORWARD) template iterator emplace(Args&&... args) { - return iterator(base.insert(std::forward(args)...)); + return iterator(base.emplace(std::forward(args)...)); } template iterator emplace_hint(const_iterator hint, Args&&... args) { - return iterator(base.insert_hint(get(hint), std::forward(args)...)); + return iterator(base.emplace_hint(get(hint), std::forward(args)...)); } +#else + + iterator emplace(value_type const& v = value_type()) + { + return iterator(base.emplace(v)); + } + + iterator emplace_hint(const_iterator hint, value_type const& v = value_type()) + { + return iterator(base.emplace_hint(get(hint), v)); + } + +#define BOOST_UNORDERED_EMPLACE(z, n, _) \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + iterator emplace( \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + return iterator( \ + base.emplace( \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + )); \ + } \ + \ + template < \ + BOOST_UNORDERED_TEMPLATE_ARGS(z, n) \ + > \ + iterator emplace_hint(const_iterator hint, \ + BOOST_UNORDERED_FUNCTION_PARAMS(z, n) \ + ) \ + { \ + return iterator(base.emplace_hint(get(hint), \ + BOOST_UNORDERED_CALL_PARAMS(z, n) \ + )); \ + } + + BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT, + BOOST_UNORDERED_EMPLACE, _) + +#undef BOOST_UNORDERED_EMPLACE + #endif iterator insert(const value_type& obj) { - return iterator(base.insert(obj)); + return iterator(base.emplace(obj)); } iterator insert(const_iterator hint, const value_type& obj) { - return iterator(base.insert_hint(get(hint), obj)); + return iterator(base.emplace_hint(get(hint), obj)); } template diff --git a/test/helpers/count.hpp b/test/helpers/count.hpp index 11e00e9f..0589586e 100644 --- a/test/helpers/count.hpp +++ b/test/helpers/count.hpp @@ -6,6 +6,8 @@ #if !defined(BOOST_UNORDERED_TEST_HELPERS_COUNT_HEAD) #define BOOST_UNORDERED_TEST_HELPERS_COUNT_HEAD +#include + namespace test { struct object_count { int instances; @@ -36,6 +38,11 @@ namespace test { bool operator!=(object_count const& x) const { return !(*this == x); } + + friend std::ostream& operator<<(std::ostream& out, object_count const& c) { + out<<"[instances: "< diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp index 25f783ec..22119e85 100644 --- a/test/objects/exception.hpp +++ b/test/objects/exception.hpp @@ -347,7 +347,7 @@ namespace exception detail::tracker.track_construct((void*) p, sizeof(T), tag_); } -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) +#if defined(BOOST_UNORDERED_STD_FORWARD) template void construct(pointer p, Args&&... args) { UNORDERED_SCOPE(allocator::construct(pointer, Args&&...)) { UNORDERED_EPOINT("Mock allocator construct function."); diff --git a/test/objects/minimal.hpp b/test/objects/minimal.hpp index 535a7684..252e3564 100644 --- a/test/objects/minimal.hpp +++ b/test/objects/minimal.hpp @@ -229,7 +229,7 @@ namespace minimal void construct(pointer p, T const& t) { new((void*)p.ptr_) T(t); } -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) +#if defined(BOOST_UNORDERED_STD_FORWARD) template void construct(pointer p, Args&&... args) { new((void*)p.ptr_) T(std::forward(args)...); } diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 28fc5e53..63f7c91b 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -218,7 +218,7 @@ namespace test new(p) T(t); } -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) +#if defined(BOOST_UNORDERED_STD_FORWARD) template void construct(pointer p, Args&&... args) { detail::tracker.track_construct((void*) p, sizeof(T), tag_); new(p) T(std::forward(args)...); diff --git a/test/unordered/compile_tests.hpp b/test/unordered/compile_tests.hpp index 486d3e2e..3dd00fdf 100644 --- a/test/unordered/compile_tests.hpp +++ b/test/unordered/compile_tests.hpp @@ -151,14 +151,12 @@ void unordered_map_test(X& r, Key const& k, T const& v) r.insert(std::pair(k, v)); -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) Key k_lvalue(k); T v_lvalue(v); r.emplace(k, v); r.emplace(k_lvalue, v_lvalue); r.emplace(rvalue(k), rvalue(v)); -#endif } template @@ -175,9 +173,7 @@ void unordered_unique_test(X& r, T const& t) { typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; test::check_return_type >::equals(r.insert(t)); -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) test::check_return_type >::equals(r.emplace(t)); -#endif } template @@ -185,9 +181,7 @@ void unordered_equivalent_test(X& r, T const& t) { typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; test::check_return_type::equals(r.insert(t)); -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) test::check_return_type::equals(r.emplace(t)); -#endif } template @@ -289,9 +283,7 @@ void unordered_test(X&, Key& k, T& t, Hash& hf, Pred& eq) const_iterator q = a.cbegin(); test::check_return_type::equals(a.insert(q, t)); -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) test::check_return_type::equals(a.emplace_hint(q, t)); -#endif a.insert(i, j); test::check_return_type::equals(a.erase(k)); diff --git a/test/unordered/unnecessary_copy_tests.cpp b/test/unordered/unnecessary_copy_tests.cpp index df4baf3c..3cf707ef 100644 --- a/test/unordered/unnecessary_copy_tests.cpp +++ b/test/unordered/unnecessary_copy_tests.cpp @@ -68,12 +68,22 @@ namespace unnecessary_copy_tests #define COPY_COUNT(n) \ if(count_copies::copies != n) { \ BOOST_ERROR("Wrong number of copies."); \ - std::cerr<<"Number of copies: "< b) { \ + BOOST_ERROR("Wrong number of copies."); \ + std::cerr<<"Number of copies: "< b) { \ + BOOST_ERROR("Wrong number of moves."); \ + std::cerr<<"Number of moves: "< void unnecessary_copy_emplace_test(T*) { @@ -117,9 +126,19 @@ namespace unnecessary_copy_tests reset(); T x; x.emplace(source()); +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) COPY_COUNT(1); +#else + COPY_COUNT(2); +#endif } + UNORDERED_TEST(unnecessary_copy_emplace_test, + ((set)(multiset)(map)(multimap))) + UNORDERED_TEST(unnecessary_copy_emplace_rvalue_test, + ((set)(multiset)(map)(multimap))) + +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) template void unnecessary_copy_emplace_move_test(T*) { @@ -131,13 +150,11 @@ namespace unnecessary_copy_tests COPY_COUNT(1); MOVE_COUNT(1); } - UNORDERED_TEST(unnecessary_copy_emplace_test, - ((set)(multiset)(map)(multimap))) - UNORDERED_TEST(unnecessary_copy_emplace_rvalue_test, - ((set)(multiset)(map)(multimap))) UNORDERED_TEST(unnecessary_copy_emplace_move_test, ((set)(multiset)(map)(multimap))) +#endif + UNORDERED_AUTO_TEST(unnecessary_copy_emplace_set_test) { reset(); @@ -172,10 +189,12 @@ namespace unnecessary_copy_tests x.emplace(source()); COPY_COUNT(1); MOVE_COUNT(0); +#if defined(BOOST_HAS_RVALUE_REFS) // No move should take place. reset(); x.emplace(std::move(a)); COPY_COUNT(0); MOVE_COUNT(0); +#endif // Just in case a did get moved... count_copies b; @@ -192,8 +211,12 @@ namespace unnecessary_copy_tests // The container will have to create b copy in order to compare with // the existing element. + // + // Note to self: If copy_count == 0 it's an error not an optimization. + // TODO: Devise a better test. reset(); + x.emplace(b, b); COPY_COUNT(1); MOVE_COUNT(0); } @@ -230,24 +253,22 @@ namespace unnecessary_copy_tests x.emplace(source >()); COPY_COUNT(2); MOVE_COUNT(0); - count_copies part; - reset(); - std::pair a_ref(part, part); - x.emplace(a_ref); - COPY_COUNT(0); MOVE_COUNT(0); + // TODO: This doesn't work on older versions of gcc. + //count_copies part; + std::pair b; + //reset(); + //std::pair a_ref(part, part); + //x.emplace(a_ref); + //COPY_COUNT(0); MOVE_COUNT(0); +#if defined(BOOST_HAS_RVALUE_REFS) // No move should take place. + // (since a is already in the container) reset(); x.emplace(std::move(a)); COPY_COUNT(0); MOVE_COUNT(0); +#endif - // Just in case a did get moved - std::pair b; - - // This test requires a C++0x std::pair. Which gcc hasn't got yet. - //reset(); - //x.emplace(b.first.tag_); - //COPY_COUNT(2); MOVE_COUNT(0); // // 2 arguments @@ -267,10 +288,9 @@ namespace unnecessary_copy_tests COPY_COUNT(1); MOVE_COUNT(0); reset(); - x.emplace(b.first.tag_, b.second.tag_); - COPY_COUNT(2); MOVE_COUNT(0); + x.emplace(count_copies(b.first.tag_), count_copies(b.second.tag_)); + COPY_COUNT(2); MOVE_COUNT(0); } -#endif } RUN_TESTS()