diff --git a/doc/ref.xml b/doc/ref.xml index 80480ed5..08b061ac 100644 --- a/doc/ref.xml +++ b/doc/ref.xml @@ -262,6 +262,60 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + + + Args&&... + + std::pair<iterator, bool> + + Inserts an object, constructed with the arguments args, in the container if and only if there is no element in the container with an equivalent value. + + + The bool component of the return type is true if an insert took place. + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent value. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + + const_iterator + + + Args&&... + + iterator + + Inserts an object, constructed with the arguments args, in the container if and only if there is no element in the container with an equivalent value. + hint is a suggestion to where the element should be inserted. + + + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent value. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + value_type const& @@ -892,6 +946,59 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + + + Args&&... + + iterator + + Inserts an object, constructed with the arguments args, in the container. + + + An iterator pointing to the inserted element. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + + const_iterator + + + Args&&... + + iterator + + Inserts an object, constructed with the arguments args, in the container. + hint is a suggestion to where the element should be inserted. + + + An iterator pointing to the inserted element. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + value_type const& @@ -1533,6 +1640,60 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + + + Args&&... + + std::pair<iterator, bool> + + Inserts an object, constructed with the arguments args, in the container if and only if there is no element in the container with an equivalent key. + + + The bool component of the return type is true if an insert took place. + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + + const_iterator + + + Args&&... + + iterator + + Inserts an object, constructed with the arguments args, in the container if and only if there is no element in the container with an equivalent key. + hint is a suggestion to where the element should be inserted. + + + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + value_type const& @@ -2208,6 +2369,59 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + + + Args&&... + + iterator + + Inserts an object, constructed with the arguments args, in the container. + + + An iterator pointing to the inserted element. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + + const_iterator + + + Args&&... + + iterator + + Inserts an object, constructed with the arguments args, in the container. + hint is a suggestion to where the element should be inserted. + + + An iterator pointing to the inserted element. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + value_type const& diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index af94679a..b238c7a7 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -32,6 +32,13 @@ #include +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) +#include +#include +#include +#include +#endif + #if BOOST_WORKAROUND(__BORLANDC__, <= 0x0582) #define BOOST_UNORDERED_BORLAND_BOOL(x) (bool)(x) #else diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index e7a2da24..b07cf369 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -108,15 +108,114 @@ namespace boost { struct node : node_base { public: +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + template + node(Args&&... args) + : node_base(), value_(std::forward(args)...) {} +#else node(value_type const& v) : node_base(), value_(v) {} +#endif value_type value_; }; +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + + // allocators + // + // Stores all the allocators that we're going to need. + + struct allocators + { + node_allocator node_alloc_; + bucket_allocator bucket_alloc_; + + allocators(value_allocator const& a) + : node_alloc_(a), bucket_alloc_(a) + {} + + void destroy(link_ptr ptr) + { + node_ptr n(node_alloc_.address(*static_cast(&*ptr))); + node_alloc_.destroy(n); + node_alloc_.deallocate(n, 1); + } + + void swap(allocators& x) + { + unordered_detail::hash_swap(node_alloc_, x.node_alloc_); + unordered_detail::hash_swap(bucket_alloc_, x.bucket_alloc_); + } + + bool operator==(allocators const& x) + { + return node_alloc_ == x.node_alloc_; + } + }; + // node_constructor // // Used to construct nodes in an exception safe manner. + class node_constructor + { + allocators& allocators_; + + node_ptr node_; + bool node_constructed_; + + public: + + node_constructor(allocators& a) + : allocators_(a), + node_(), node_constructed_(false) + { + } + + ~node_constructor() + { + if (node_) { + if (node_constructed_) + allocators_.node_alloc_.destroy(node_); + allocators_.node_alloc_.deallocate(node_, 1); + } + } + + template + void construct(Args&&... args) + { + BOOST_ASSERT(!node_); + node_constructed_ = false; + + node_ = allocators_.node_alloc_.allocate(1); + allocators_.node_alloc_.construct(node_, std::forward(args)...); + node_constructed_ = true; + } + + node_ptr get() const + { + BOOST_ASSERT(node_); + return node_; + } + + // no throw + link_ptr release() + { + node_ptr p = node_; + unordered_detail::reset(node_); + return link_ptr(allocators_.bucket_alloc_.address(*p)); + } + + private: + node_constructor(node_constructor const&); + node_constructor& operator=(node_constructor const&); + }; +#else + + // allocators + // + // Stores all the allocators that we're going to need. + struct allocators { node_allocator node_alloc_; @@ -151,6 +250,10 @@ namespace boost { } }; + // node_constructor + // + // Used to construct nodes in an exception safe manner. + class node_constructor { allocators& allocators_; @@ -201,6 +304,27 @@ namespace boost { value_constructed_ = true; } +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + template + void construct(Args&&... args) + { + BOOST_ASSERT(!node_); + value_constructed_ = false; + node_base_constructed_ = false; + + node_ = allocators_.node_alloc_.allocate(1); + + allocators_.node_base_alloc_.construct( + allocators_.node_base_alloc_.address(*node_), + node_base()); + node_base_constructed_ = true; + + allocators_.value_alloc_.construct( + allocators_.value_alloc_.address(node_->value_), std::forward(args)...); + value_constructed_ = true; + } +#endif + node_ptr get() const { BOOST_ASSERT(node_); @@ -219,6 +343,7 @@ namespace boost { node_constructor(node_constructor const&); node_constructor& operator=(node_constructor const&); }; +#endif // Methods for navigating groups of elements with equal keys. @@ -1251,9 +1376,9 @@ namespace boost { // accessors // no throw - value_allocator get_allocator() const + node_allocator get_allocator() const { - return data_.allocators_.value_alloc_; + return data_.allocators_.node_alloc_; } // no throw @@ -1356,26 +1481,6 @@ namespace boost { return need_to_reserve; } - // basic exception safety - // - // This version of reserve is called when inserting a range - // into a container with equivalent keys, it creates more buckets - // if the resulting load factor would be over 80% of the load - // factor. This is to try to avoid excessive rehashes. - bool reserve_extra(size_type n) - { - using namespace std; - - bool need_to_reserve = n >= max_load_; - // throws - basic: - if (need_to_reserve) { - rehash_impl(double_to_size_t(floor( - n / (double) mlf_ * 1.25)) + 1); - } - BOOST_ASSERT(n < max_load_ || n > max_size()); - return need_to_reserve; - } - public: // no throw @@ -1422,6 +1527,41 @@ 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&...) + { + return k; + } + + 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) + { + return v.first; + } + + template + static no_key extract_key(Args const&...) + { + return no_key(); + } +#endif + public: // if hash function throws, basic exception safety @@ -1526,16 +1666,70 @@ namespace boost { // strong otherwise iterator_base insert(value_type const& v) { - 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 position = find_iterator(bucket, k); - // 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); + return insert_impl(a); + } + + // Insert (equivalent key containers) + + // if hash function throws, basic exception safety + // strong otherwise + iterator_base insert_hint(iterator_base const& it, value_type const& v) + { + // 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); + + return insert_hint_impl(it, a); + } + +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + // 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); + } + + // 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); + } + +#endif + + iterator_base insert_impl(node_constructor& a) + { + key_type const& k = extract_key(a.get()->value_); + size_type hash_value = hash_function()(k); + bucket_ptr bucket = data_.bucket_ptr_from_hash(hash_value); + link_ptr position = find_iterator(bucket, k); + // reserve has basic exception safety if the hash function // throws, strong otherwise. if(reserve(size() + 1)) @@ -1550,17 +1744,13 @@ namespace boost { ); } - // Insert (equivalent key containers) - - // if hash function throws, basic exception safety - // strong otherwise - iterator_base insert_hint(iterator_base const& it, value_type const& v) + iterator_base insert_hint_impl(iterator_base const& it, node_constructor& a) { // equal can throw, but with no effects - if (it == data_.end() || !equal(extract_key(v), *it)) { + if (it == data_.end() || !equal(extract_key(a.get()->value_), *it)) { // Use the standard insert if the iterator doesn't point // to a matching key. - return insert(v); + return insert_impl(a); } else { // Find the first node in the group - so that the node @@ -1570,15 +1760,10 @@ namespace boost { while(data_.prev_in_group(start)->next_ == start) start = data_.prev_in_group(start); - // 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. bucket_ptr base = reserve(size() + 1) ? - get_bucket(extract_key(v)) : it.bucket_; + get_bucket(extract_key(a.get()->value_)) : it.bucket_; // Nothing after this point can throw @@ -1602,7 +1787,7 @@ namespace boost { } else { // Only require basic exception safety here - reserve_extra(size() + distance); + reserve(size() + distance); node_constructor a(data_.allocators_); for (; i != j; ++i) { @@ -1729,6 +1914,104 @@ namespace boost { return insert(v).first; } +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + // 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. + + // if hash function throws, basic exception safety + // strong otherwise + template + std::pair insert(Args&&... args) + { + return insert_impl( + extract_key(std::forward(args)...), + std::forward(args)...); + } + + template + std::pair insert_impl(key_type const& k, Args&&... args) + { + // No side effects in this initial code + 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(std::forward(args)...); + + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + if(reserve(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); + } + } + + template + std::pair insert_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)...); + + // No side effects in this initial code + key_type const& k = extract_key(a.get()->value_); + 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 { + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + if(reserve(size() + 1)) + bucket = data_.bucket_ptr_from_hash(hash_value); + + // Nothing after this point can throw. + + return std::pair(iterator_base(bucket, + data_.link_node_in_bucket(a, bucket)), true); + } + } + + // 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& it, Args&&... args) + { + // Life is complicated - just call the normal implementation. + return insert(std::forward(args)...).first; + } +#endif + // Insert from iterators (unique keys) template diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp index 99b88a7f..21765eaa 100644 --- a/include/boost/unordered_map.hpp +++ b/include/boost/unordered_map.hpp @@ -199,6 +199,21 @@ namespace boost // modifiers +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + template + std::pair emplace(Args&&... args) + { + return boost::unordered_detail::pair_cast( + base.insert(std::forward(args)...)); + } + + template + iterator emplace(const_iterator hint, Args&&... args) + { + return iterator(base.insert_hint(get(hint), std::forward(args)...)); + } +#endif + std::pair insert(const value_type& obj) { return boost::unordered_detail::pair_cast( @@ -553,6 +568,20 @@ namespace boost // modifiers +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + template + iterator emplace(Args&&... args) + { + return iterator(base.insert(std::forward(args)...)); + } + + template + iterator emplace(const_iterator hint, Args&&... args) + { + return iterator(base.insert_hint(get(hint), std::forward(args)...)); + } +#endif + iterator insert(const value_type& obj) { return iterator(base.insert(obj)); diff --git a/include/boost/unordered_set.hpp b/include/boost/unordered_set.hpp index 5f3c5df6..bd4ceb1c 100644 --- a/include/boost/unordered_set.hpp +++ b/include/boost/unordered_set.hpp @@ -196,6 +196,22 @@ namespace boost // modifiers +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + template + std::pair emplace(Args&&... args) + { + return boost::unordered_detail::pair_cast( + base.insert(std::forward(args)...)); + } + + template + iterator emplace(const_iterator hint, Args&&... args) + { + return iterator( + base.insert_hint(get(hint), std::forward(args)...)); + } +#endif + std::pair insert(const value_type& obj) { return boost::unordered_detail::pair_cast( @@ -520,6 +536,20 @@ namespace boost // modifiers +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + template + iterator emplace(Args&&... args) + { + return iterator(base.insert(std::forward(args)...)); + } + + template + iterator emplace(const_iterator hint, Args&&... args) + { + return iterator(base.insert_hint(get(hint), std::forward(args)...)); + } +#endif + iterator insert(const value_type& obj) { return iterator(base.insert(obj)); diff --git a/test/exception/insert_exception_tests.cpp b/test/exception/insert_exception_tests.cpp index 47fd2db7..26234adc 100644 --- a/test/exception/insert_exception_tests.cpp +++ b/test/exception/insert_exception_tests.cpp @@ -36,6 +36,25 @@ struct insert_test_base : public test::exception_base } }; +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + +template +struct emplace_test1 : public insert_test_base +{ + typedef BOOST_DEDUCED_TYPENAME insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + for(BOOST_DEDUCED_TYPENAME test::random_values::const_iterator + it = this->values.begin(), end = this->values.end(); it != end; ++it) + { + strong.store(x); + x.emplace(*it); + } + } +}; + +#endif + template struct insert_test1 : public insert_test_base { @@ -204,7 +223,15 @@ struct insert_test_rehash3 : public insert_test_base } }; -RUN_EXCEPTION_TESTS( - (insert_test1)(insert_test2)(insert_test3)(insert_test4) - (insert_test_rehash1)(insert_test_rehash2)(insert_test_rehash3), - CONTAINER_SEQ) +#define BASIC_TESTS \ + (insert_test1)(insert_test2)(insert_test3)(insert_test4) \ + (insert_test_rehash1)(insert_test_rehash2)(insert_test_rehash3) + +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) +#define ALL_TESTS (emplace_test1)BASIC_TESTS +#else +#define ALL_TESTS BASIC_TESTS +#endif + + +RUN_EXCEPTION_TESTS(ALL_TESTS, CONTAINER_SEQ) diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp index f5fd420c..9e10c4e4 100644 --- a/test/objects/exception.hpp +++ b/test/objects/exception.hpp @@ -347,6 +347,16 @@ namespace exception detail::tracker.track_construct((void*) p, sizeof(T), tag_); } +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + template void construct(pointer p, Args&&... args) { + UNORDERED_SCOPE(allocator::construct(pointer, Args&&...)) { + UNORDERED_EPOINT("Mock allocator construct function."); + new(p) T(std::forward(args)...); + } + detail::tracker.track_construct((void*) p, sizeof(T), tag_); + } +#endif + void destroy(pointer p) { detail::tracker.track_destroy((void*) p, sizeof(T), tag_); p->~T(); diff --git a/test/objects/minimal.hpp b/test/objects/minimal.hpp index c39bf072..d5fe30d1 100644 --- a/test/objects/minimal.hpp +++ b/test/objects/minimal.hpp @@ -24,6 +24,7 @@ namespace minimal class copy_constructible; class default_copy_constructible; class assignable; + template class hash; template class equal_to; template class ptr; @@ -207,6 +208,13 @@ 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) + template void construct(pointer p, Args&&... args) { + new((void*)p.ptr_) T(std::forward(args)...); + } +#endif + void destroy(pointer p) { ((T*)p.ptr_)->~T(); } size_type max_size() const { return 1000; } diff --git a/test/objects/test.hpp b/test/objects/test.hpp index fd9d2921..d8555bec 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -212,6 +212,13 @@ namespace test new(p) T(t); } +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + template void construct(pointer p, Args&&... args) { + detail::tracker.track_construct((void*) p, sizeof(T), tag_); + new(p) T(std::forward(args)...); + } +#endif + void destroy(pointer p) { detail::tracker.track_destroy((void*) p, sizeof(T), tag_); p->~T(); diff --git a/test/unordered/compile_tests.hpp b/test/unordered/compile_tests.hpp index d7843cdf..b4f561b6 100644 --- a/test/unordered/compile_tests.hpp +++ b/test/unordered/compile_tests.hpp @@ -25,6 +25,7 @@ typedef long double comparison_type; template void sink(T const&) {} +template T rvalue(T const& v) { return v; } template void container_test(X& r, T&) @@ -144,11 +145,20 @@ void unordered_set_test(X&, Key const&) } template -void unordered_map_test(X&, Key const&, T const&) +void unordered_map_test(X& r, Key const& k, T const& v) { typedef BOOST_DEDUCED_TYPENAME X::value_type value_type; typedef BOOST_DEDUCED_TYPENAME X::key_type key_type; BOOST_MPL_ASSERT((boost::is_same >)); + +#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 @@ -156,6 +166,9 @@ 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 @@ -163,6 +176,9 @@ 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 @@ -264,6 +280,9 @@ 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(q, t)); +#endif a.insert(i, j); test::check_return_type::equals(a.erase(k)); diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index 479908c7..5d1f2fb8 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -218,6 +218,74 @@ void insert_tests2(X*, test::random_generator generator = test::default_generato } } +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + +template +void unique_emplace_tests1(X*, test::random_generator generator = test::default_generator) +{ + typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; + typedef test::ordered ordered; + + std::cerr<<"emplace(value) tests for containers with unique keys.\n"; + + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000, generator); + + for(BOOST_DEDUCED_TYPENAME test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + std::pair r1 = x.emplace(*it); + std::pair r2 = tracker.insert(*it); + + BOOST_CHECK(r1.second == r2.second); + BOOST_CHECK(*r1.first == *r2.first); + + tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_CHECK(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); +} + +template +void equivalent_emplace_tests1(X*, test::random_generator generator = test::default_generator) +{ + std::cerr<<"emplace(value) tests for containers with equivalent keys.\n"; + + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000, generator); + for(BOOST_DEDUCED_TYPENAME test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + BOOST_DEDUCED_TYPENAME X::iterator r1 = x.emplace(*it); + BOOST_DEDUCED_TYPENAME test::ordered::iterator r2 = tracker.insert(*it); + + BOOST_CHECK(*r1 == *r2); + + tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_CHECK(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); +} + +#endif + template void map_tests(X*, test::random_generator generator = test::default_generator) { @@ -283,6 +351,18 @@ UNORDERED_TEST(insert_tests2, ((default_generator)(generate_collisions)) ) +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) +UNORDERED_TEST(unique_emplace_tests1, + ((test_set)(test_map)) + ((default_generator)(generate_collisions)) +) + +UNORDERED_TEST(equivalent_emplace_tests1, + ((test_multiset)(test_multimap)) + ((default_generator)(generate_collisions)) +) +#endif + UNORDERED_TEST(map_tests, ((test_map)) ((default_generator)(generate_collisions)) diff --git a/test/unordered/unnecessary_copy_tests.cpp b/test/unordered/unnecessary_copy_tests.cpp index 64039020..f28daed3 100644 --- a/test/unordered/unnecessary_copy_tests.cpp +++ b/test/unordered/unnecessary_copy_tests.cpp @@ -11,15 +11,34 @@ namespace unnecessary_copy_tests { struct count_copies { - static int count; - count_copies() { ++count; } - count_copies(count_copies const&) { ++count; } + static int copies; + static int moves; + count_copies() : tag_(0) { ++copies; } + explicit count_copies(int tag) : tag_(tag) { ++copies; } + count_copies(count_copies const&, count_copies const& x) : tag_(x.tag_) { ++copies; } + count_copies(count_copies const& x) : tag_(x.tag_) { ++copies; } +#if defined(BOOST_HAS_RVALUE_REFS) + count_copies(count_copies&& x) : tag_(x.tag_) { + x.tag_ = -1; ++moves; + } +#endif + int tag_; private: count_copies& operator=(count_copies const&); }; - bool operator==(count_copies const&, count_copies const&) { - return true; + bool operator==(count_copies const& x, count_copies const& y) { + return x.tag_ == y.tag_; + } + + template + T source() { + return T(); + } + + void reset() { + count_copies::copies = 0; + count_copies::moves = 0; } } @@ -29,29 +48,36 @@ namespace boost namespace unnecessary_copy_tests #endif { - std::size_t hash_value(unnecessary_copy_tests::count_copies const&) { - return 0; + std::size_t hash_value(unnecessary_copy_tests::count_copies const& x) { + return x.tag_; } } +#define COPY_COUNT(n) \ + if(count_copies::copies != n) { \ + BOOST_ERROR("Wrong number of copies."); \ + std::cerr<<"Number of copies: "< - void unnecessary_copy_test(T*) + void unnecessary_copy_insert_test(T*) { - count_copies::count = 0; + reset(); T x; BOOST_DEDUCED_TYPENAME T::value_type a; - BOOST_CHECK(count_copies::count == 1); - if(count_copies::count != 1) - std::cerr<* set; @@ -59,7 +85,181 @@ namespace unnecessary_copy_tests boost::unordered_map* map; boost::unordered_multimap* multimap; - UNORDERED_TEST(unnecessary_copy_test, ((set)(multiset)(map)(multimap))) + UNORDERED_TEST(unnecessary_copy_insert_test, + ((set)(multiset)(map)(multimap))) + +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) + template + void unnecessary_copy_emplace_test(T*) + { + reset(); + T x; + BOOST_DEDUCED_TYPENAME T::value_type a; + COPY_COUNT(1); + x.emplace(a); + COPY_COUNT(2); + } + + template + void unnecessary_copy_emplace_rvalue_test(T*) + { + reset(); + T x; + x.emplace(source()); + COPY_COUNT(1); + } + + template + void unnecessary_copy_emplace_move_test(T*) + { + reset(); + T x; + BOOST_DEDUCED_TYPENAME T::value_type a; + COPY_COUNT(1); MOVE_COUNT(0); + x.emplace(std::move(a)); + 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))) + + UNORDERED_AUTO_TEST(unnecessary_copy_emplace_set_test) + { + reset(); + boost::unordered_set x; + count_copies a; + x.insert(a); + COPY_COUNT(2); MOVE_COUNT(0); + + // + // 0 arguments + // + + // The container will have to create a copy in order to compare with + // the existing element. + reset(); + x.emplace(); + COPY_COUNT(1); MOVE_COUNT(0); + + // + // 1 argument + // + + // Emplace should be able to tell that there already is an element + // without creating a new one. + reset(); + x.emplace(a); + COPY_COUNT(0); MOVE_COUNT(0); + + // A new object is created by source, but it shouldn't be moved or + // copied. + reset(); + x.emplace(source()); + COPY_COUNT(1); MOVE_COUNT(0); + + // No move should take place. + reset(); + x.emplace(std::move(a)); + COPY_COUNT(0); MOVE_COUNT(0); + + // Just in case a did get moved... + count_copies b; + + // The container will have to create a copy in order to compare with + // the existing element. + reset(); + x.emplace(b.tag_); + COPY_COUNT(1); MOVE_COUNT(0); + + // + // 2 arguments + // + + // The container will have to create b copy in order to compare with + // the existing element. + + reset(); + x.emplace(b, b); + COPY_COUNT(1); MOVE_COUNT(0); + } + + UNORDERED_AUTO_TEST(unnecessary_copy_emplace_map_test) + { + reset(); + boost::unordered_map x; + // TODO: Run tests for pairs without const etc. + std::pair a; + x.emplace(a); + COPY_COUNT(4); MOVE_COUNT(0); + + // + // 0 arguments + // + + // COPY_COUNT(1) would be okay here. + reset(); + x.emplace(); + COPY_COUNT(2); MOVE_COUNT(0); + + // + // 1 argument + // + + reset(); + x.emplace(a); + COPY_COUNT(0); MOVE_COUNT(0); + + // A new object is created by source, but it shouldn't be moved or + // copied. + reset(); + 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); + + // No move should take place. + reset(); + x.emplace(std::move(a)); + COPY_COUNT(0); MOVE_COUNT(0); + + // 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 + // + + reset(); + x.emplace(b.first, b.second); + COPY_COUNT(0); MOVE_COUNT(0); + + reset(); + x.emplace(source(), source()); + COPY_COUNT(2); MOVE_COUNT(0); + + // source creates a single copy. + reset(); + x.emplace(b.first, source()); + COPY_COUNT(1); MOVE_COUNT(0); + + reset(); + x.emplace(b.first.tag_, b.second.tag_); + COPY_COUNT(2); MOVE_COUNT(0); + } +#endif } RUN_TESTS()