diff --git a/include/boost/unordered/detail/buckets.hpp b/include/boost/unordered/detail/buckets.hpp index 927ae534..df79ccf6 100644 --- a/include/boost/unordered/detail/buckets.hpp +++ b/include/boost/unordered/detail/buckets.hpp @@ -114,6 +114,17 @@ namespace boost { namespace unordered { namespace detail { size_(), allocators_(b.allocators_) { + swap(b); + } + + template + buckets(table& x, move_tag) + : buckets_(), + bucket_count_(x.bucket_count_), + allocators_(x.allocators_) + { + swap(x); + x.size_ = 0; } inline ~buckets() @@ -152,10 +163,10 @@ namespace boost { namespace unordered { namespace detail { std::swap(size_, other.size_); } - void move(buckets& other) + void move_buckets_from(buckets& other) { BOOST_ASSERT(node_alloc() == other.node_alloc()); - if(this->buckets_) { this->delete_buckets(); } + BOOST_ASSERT(!this->buckets_); this->buckets_ = other.buckets_; this->bucket_count_ = other.bucket_count_; this->size_ = other.size_; @@ -754,6 +765,7 @@ namespace boost { namespace unordered { namespace detail { node_ptr first_node = a.release(); node::set_hash(first_node, hash); node_ptr end = prev->next_ = first_node; + ++dst.size_; for(n = n->next_; n != group_end; n = n->next_) { a.construct(boost::move(node::get_value(n))); diff --git a/include/boost/unordered/detail/node.hpp b/include/boost/unordered/detail/node.hpp index 4beddfa2..060cfd30 100644 --- a/include/boost/unordered/detail/node.hpp +++ b/include/boost/unordered/detail/node.hpp @@ -21,6 +21,11 @@ namespace boost { namespace unordered { namespace detail { + // Some forward declarations for buckets and tables + + template class table; + template class buckets; + //////////////////////////////////////////////////////////////////////////// // // This section implements buckets and nodes. Here's a rough diff --git a/include/boost/unordered/detail/table.hpp b/include/boost/unordered/detail/table.hpp index ecbf4dfe..f55c8afd 100644 --- a/include/boost/unordered/detail/table.hpp +++ b/include/boost/unordered/detail/table.hpp @@ -43,7 +43,7 @@ namespace boost { namespace unordered { namespace detail { // Members float mlf_; - std::size_t max_load_; + std::size_t max_load_; // Only use if this->buckets_. // Helper methods @@ -195,20 +195,18 @@ namespace boost { namespace unordered { namespace detail { table(table& x, move_tag m) : buckets(x, m), functions(x), - mlf_(1.0f), - max_load_(0) - { - this->partial_swap(x); - } + mlf_(x.mlf_), + max_load_(this->buckets_ ? calculate_max_load() : 0) {} + // TODO: Why do I use x's bucket count? table(table& x, node_allocator const& a, move_tag m) : buckets(a, x.bucket_count_), functions(x), mlf_(x.mlf_), - max_load_(0) + max_load_(x.max_load_) { if(a == x.node_alloc()) { - this->partial_swap(x); + this->buckets::swap(x, false_type()); } else if(x.size_) { // Use a temporary table because move_buckets_to leaves the @@ -230,44 +228,75 @@ namespace boost { namespace unordered { namespace detail { } void assign(table const& x) + { + assign(x, integral_constant:: + propagate_on_container_copy_assignment::value>()); + } + + void assign(table const& x, false_type) { table tmp(x, this->node_alloc()); - this->fast_swap(tmp); + this->swap(tmp, false_type()); + } + + void assign(table const& x, true_type) + { + table tmp(x, x.node_alloc()); + // Need to delete before setting the allocator so that buckets + // aren't deleted with the wrong allocator. + if(this->buckets_) this->delete_buckets(); + // TODO: Can allocator assignment throw? + this->allocators_ = x.allocators_; + this->swap(tmp, false_type()); } void move_assign(table& x) { - // This can throw, but it only affects the function objects - // that aren't in use so it is strongly exception safe, via. - // double buffering. - set_hash_functions new_func_this(*this, x); - + move_assign(x, integral_constant:: + propagate_on_container_move_assignment::value>()); + } + + void move_assign(table& x, true_type) + { + if(this->buckets_) this->delete_buckets(); + this->allocators_ = x.allocators_; // TODO: Move allocators, not copy. + move_assign_no_alloc(x); + } + + void move_assign(table& x, false_type) + { if(this->node_alloc() == x.node_alloc()) { - this->buckets::move(x); // no throw - this->mlf_ = x.mlf_; - this->max_load_ = x.max_load_; + if(this->buckets_) this->delete_buckets(); + move_assign_no_alloc(x); } else { - // Create new buckets in separate buckets - // which will clean up if anything throws an exception. - - buckets b(this->node_alloc(), x.min_buckets_for_size(x.size_)); + set_hash_functions new_func_this(*this, x); if (x.size_) { - // Use a temporary table because move_buckets_to leaves the - // source container in a complete mess. + buckets b(this->node_alloc(), x.min_buckets_for_size(x.size_)); buckets tmp(x, move_tag()); tmp.move_buckets_to(b); b.swap(*this); - this->mlf_ = x.mlf_; - this->max_load_ = calculate_max_load(); } else { - b.swap(*this); - this->mlf_ = x.mlf_; + this->clear(); } + + this->mlf_ = x.mlf_; + if (this->buckets_) this->max_load_ = calculate_max_load(); + new_func_this.commit(); } - + } + + void move_assign_no_alloc(table& x) + { + set_hash_functions new_func_this(*this, x); + // No throw from here. + this->move_buckets_from(x); + this->mlf_ = x.mlf_; + this->max_load_ = x.max_load_; new_func_this.commit(); } @@ -276,40 +305,28 @@ namespace boost { namespace unordered { namespace detail { void swap(table& x) { - // TODO: Should I actually swap the buckets even if this - // is with self? - if(this != &x) { - { - set_hash_functions op1(*this, x); - set_hash_functions op2(x, *this); - - this->buckets::swap(x, integral_constant:: - propagate_on_container_swap::value>()); - - op1.commit(); - op2.commit(); - } - - std::swap(this->mlf_, x.mlf_); - std::swap(this->max_load_, x.max_load_); - } + swap(x, integral_constant:: + propagate_on_container_swap::value>()); } - // Swap everything but the allocators - void fast_swap(table& x) + // Only swaps the allocators if Propagate::value + template + void swap(table& x, Propagate p) { - { - set_hash_functions op1(*this, x); - set_hash_functions op2(x, *this); - op1.commit(); - op2.commit(); - } - partial_swap(x); + set_hash_functions op1(*this, x); + set_hash_functions op2(x, *this); + // I think swap can throw if Propagate::value, + // since the allocators' swap can throw. Not sure though. + this->buckets::swap(x, p); + std::swap(this->mlf_, x.mlf_); + std::swap(this->max_load_, x.max_load_); + op1.commit(); + op2.commit(); } // Swap everything but the allocators, and the functions objects. - void partial_swap(table& x) + void swap_contents(table& x) { this->buckets::swap(x, false_type()); std::swap(this->mlf_, x.mlf_); @@ -393,24 +410,22 @@ namespace boost { namespace unordered { namespace detail { template inline bool table::reserve_for_insert(std::size_t size) { - if(size >= max_load_) { - if (!this->buckets_) { - std::size_t old_bucket_count = this->bucket_count_; - this->bucket_count_ = (std::max)(this->bucket_count_, - this->min_buckets_for_size(size)); - this->create_buckets(); + if (!this->buckets_) { + std::size_t old_bucket_count = this->bucket_count_; + this->bucket_count_ = (std::max)(this->bucket_count_, + this->min_buckets_for_size(size)); + this->create_buckets(); + this->max_load_ = calculate_max_load(); + return old_bucket_count != this->bucket_count_; + } + else if(size >= max_load_) { + std::size_t num_buckets + = this->min_buckets_for_size((std::max)(size, + this->size_ + (this->size_ >> 1))); + if (num_buckets != this->bucket_count_) { + this->rehash_impl(num_buckets); this->max_load_ = calculate_max_load(); - return old_bucket_count != this->bucket_count_; - } - else { - std::size_t num_buckets - = this->min_buckets_for_size((std::max)(size, - this->size_ + (this->size_ >> 1))); - if (num_buckets != this->bucket_count_) { - this->rehash_impl(num_buckets); - this->max_load_ = calculate_max_load(); - return true; - } + return true; } } @@ -428,7 +443,6 @@ namespace boost { namespace unordered { namespace detail { if(!this->size_) { if(this->buckets_) this->delete_buckets(); this->bucket_count_ = next_prime(min_buckets); - this->max_load_ = 0; } else { // no throw: diff --git a/include/boost/unordered/detail/unique.hpp b/include/boost/unordered/detail/unique.hpp index d87131b1..a9d281a0 100644 --- a/include/boost/unordered/detail/unique.hpp +++ b/include/boost/unordered/detail/unique.hpp @@ -316,7 +316,14 @@ namespace boost { namespace unordered { namespace detail { void insert_range_impl(key_type const&, InputIt i, InputIt j) { node_constructor a(*this); - + + // Special case for empty buckets so that we can use + // max_load_ (which isn't valid when buckets_ is null). + if (!this->buckets_) { + insert_range_empty(a, extractor::extract(*i), i, j); + if (++i == j) return; + } + do { // Note: can't use get_key as '*i' might not be value_type - it // could be a pair with first_types as key_type without const or a @@ -329,6 +336,16 @@ namespace boost { namespace unordered { namespace detail { } while(++i != j); } + template + void insert_range_empty(node_constructor& a, key_type const& k, + InputIt i, InputIt j) + { + std::size_t hash = this->hash_function()(k); + a.construct(*i); + this->reserve_for_insert(this->size_ + insert_size(i, j)); + add_node(a, hash % this->bucket_count_, hash); + } + template void insert_range_impl2(node_constructor& a, key_type const& k, InputIt i, InputIt j) diff --git a/test/helpers/memory.hpp b/test/helpers/memory.hpp index 1402cfe4..71245c0b 100644 --- a/test/helpers/memory.hpp +++ b/test/helpers/memory.hpp @@ -137,7 +137,7 @@ namespace test } void track_deallocate(void* ptr, std::size_t n, std::size_t size, - int tag) + int tag, bool check_tag_ = true) { BOOST_DEDUCED_TYPENAME allocated_memory_type::iterator pos = allocated_memory.find( @@ -147,7 +147,7 @@ namespace test } else { BOOST_TEST(pos->first.start == ptr); BOOST_TEST(pos->first.end == (char*) ptr + n * size); - BOOST_TEST(pos->second.tag_ == tag); + if (check_tag_) BOOST_TEST(pos->second.tag_ == tag); allocated_memory.erase(pos); } BOOST_TEST(count_allocations > 0); @@ -168,6 +168,38 @@ namespace test } }; } + + namespace detail + { + // This won't be a problem as I'm only using a single compile unit + // in each test (this is actually required by the minimal test + // framework). + // + // boostinspect:nounnamed + namespace { + test::detail::memory_tracker > tracker; + } + } + + template + struct bool_type { + enum { value = Value }; + }; + + struct true_type { + enum { value = true }; + }; + + struct false_type { + enum { value = false }; + }; + + template + struct is_propagate_on_swap : false_type {}; + template + struct is_propagate_on_assign : false_type {}; + template + struct is_propagate_on_move : false_type {}; } #endif diff --git a/test/objects/cxx11_allocator.hpp b/test/objects/cxx11_allocator.hpp new file mode 100644 index 00000000..d26b7d27 --- /dev/null +++ b/test/objects/cxx11_allocator.hpp @@ -0,0 +1,210 @@ + +// Copyright 2006-2011 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) + +#if !defined(BOOST_UNORDERED_TEST_CXX11_ALLOCATOR_HEADER) +#define BOOST_UNORDERED_TEST_CXX11_ALLOCATOR_HEADER + +#include +#include +#include + +#include "../helpers/memory.hpp" + +namespace test +{ + enum allocator_flags + { + allocator_false = 0, + select_copy = 1, + propagate_swap = 2, + propagate_assign = 4, + propagate_move = 8, + allocator_flags_all = 15, + no_select_copy = allocator_flags_all - select_copy, + no_propagate_swap = allocator_flags_all - propagate_swap, + no_propagate_assign = allocator_flags_all - propagate_assign, + no_propagate_move = allocator_flags_all - propagate_move + }; + + template + struct copy_allocator_base + { + // select_on_copy goes here. + }; + + template <> + struct copy_allocator_base {}; + + template + struct swap_allocator_base + { + struct propagate_on_container_swap { + enum { value = Flag != allocator_false }; }; + }; + + template + struct assign_allocator_base + { + struct propagate_on_container_copy_assignment { + enum { value = Flag != allocator_false }; }; + }; + + template + struct move_allocator_base + { + struct propagate_on_container_move_assignment { + enum { value = Flag != allocator_false }; }; + }; + + namespace + { + // boostinspect:nounnamed + bool force_equal_allocator_value = false; + } + + struct force_equal_allocator + { + bool old_value_; + + explicit force_equal_allocator(bool value) + : old_value_(force_equal_allocator_value) + { force_equal_allocator_value = value; } + + ~force_equal_allocator() + { force_equal_allocator_value = old_value_; } + }; + + template + struct cxx11_allocator : + public copy_allocator_base, + public swap_allocator_base, + public assign_allocator_base, + public move_allocator_base + { + int tag_; + + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef T const* const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind { + typedef cxx11_allocator other; + }; + + explicit cxx11_allocator(int t = 0) : tag_(t) + { + detail::tracker.allocator_ref(); + } + + template cxx11_allocator( + cxx11_allocator const& x) + : tag_(x.tag_) + { + detail::tracker.allocator_ref(); + } + + cxx11_allocator(cxx11_allocator const& x) + : tag_(x.tag_) + { + detail::tracker.allocator_ref(); + } + + ~cxx11_allocator() + { + detail::tracker.allocator_unref(); + } + + pointer address(reference r) + { + return pointer(&r); + } + + const_pointer address(const_reference r) + { + return const_pointer(&r); + } + + pointer allocate(size_type n) { + pointer ptr(static_cast(::operator new(n * sizeof(T)))); + detail::tracker.track_allocate((void*) ptr, n, sizeof(T), tag_); + return ptr; + } + + pointer allocate(size_type n, void const* u) + { + pointer ptr(static_cast(::operator new(n * sizeof(T)))); + detail::tracker.track_allocate((void*) ptr, n, sizeof(T), tag_); + return ptr; + } + + void deallocate(pointer p, size_type n) + { + // Only checking tags when propagating swap. + // Note that tags will be tested + // properly in the normal allocator. + detail::tracker.track_deallocate((void*) p, n, sizeof(T), tag_, + (Flags & propagate_swap)); + ::operator delete((void*) p); + } + + void construct(T* p, T const& t) { + detail::tracker.track_construct((void*) p, sizeof(T), tag_); + new(p) T(t); + } + +#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE) + template void construct(T* p, Args&&... args) { + detail::tracker.track_construct((void*) p, sizeof(T), tag_); + new(p) T(std::forward(args)...); + } +#endif + + void destroy(T* p) { + detail::tracker.track_destroy((void*) p, sizeof(T), tag_); + p->~T(); + } + + size_type max_size() const { + return (std::numeric_limits::max)(); + } + + // When not propagating swap, allocators are always equal + // to avoid undefined behaviour. + bool operator==(cxx11_allocator const& x) const + { + return force_equal_allocator_value || (tag_ == x.tag_); + } + + bool operator!=(cxx11_allocator const& x) const + { + return !(*this == x); + } + }; + + template + bool equivalent_impl( + cxx11_allocator const& x, + cxx11_allocator const& y, + test::derived_type) + { + return x.tag_ == y.tag_; + } + + template + struct is_propagate_on_swap > + : bool_type<(bool)(Flags & propagate_swap)> {}; + template + struct is_propagate_on_assign > + : bool_type<(bool)(Flags & propagate_assign)> {}; + template + struct is_propagate_on_move > + : bool_type<(bool)(Flags & propagate_move)> {}; +} + +#endif diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 0db0fe56..57dec792 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -9,11 +9,9 @@ #include #include #include -#include #include "../helpers/fwd.hpp" #include "../helpers/count.hpp" #include "../helpers/memory.hpp" -#include namespace test { @@ -28,16 +26,6 @@ namespace test object generate(object const*); implicitly_convertible generate(implicitly_convertible const*); - struct true_type - { - enum { value = true }; - }; - - struct false_type - { - enum { value = false }; - }; - class object : globally_counted_object { friend class hash; @@ -195,18 +183,6 @@ namespace test } }; - namespace detail - { - // This won't be a problem as I'm only using a single compile unit - // in each test (this is actually require by the minimal test - // framework). - // - // boostinspect:nounnamed - namespace { - test::detail::memory_tracker > tracker; - } - } - template class allocator { @@ -308,12 +284,15 @@ namespace test { return tag_ != x.tag_; } - - typedef true_type propagate_on_container_copy_assignment; - typedef true_type propagate_on_container_move_assignment; - typedef true_type propagate_on_container_swap; }; + template + struct is_propagate_on_swap > : false_type {}; + template + struct is_propagate_on_assign > : false_type {}; + template + struct is_propagate_on_move > : false_type {}; + template bool equivalent_impl(allocator const& x, allocator const& y, test::derived_type) diff --git a/test/unordered/assign_tests.cpp b/test/unordered/assign_tests.cpp index 03221d5a..3e1a85aa 100644 --- a/test/unordered/assign_tests.cpp +++ b/test/unordered/assign_tests.cpp @@ -9,6 +9,7 @@ #include #include "../helpers/test.hpp" #include "../objects/test.hpp" +#include "../objects/cxx11_allocator.hpp" #include "../helpers/random_values.hpp" #include "../helpers/tracker.hpp" #include "../helpers/equivalent.hpp" @@ -68,7 +69,9 @@ void assign_tests2(T*, BOOST_DEDUCED_TYPENAME T::key_equal eq2(2); BOOST_DEDUCED_TYPENAME T::allocator_type al1(1); BOOST_DEDUCED_TYPENAME T::allocator_type al2(2); - + + typedef BOOST_DEDUCED_TYPENAME T::allocator_type allocator_type; + std::cerr<<"assign_tests2.1\n"; { test::check_instances check_; @@ -92,7 +95,14 @@ void assign_tests2(T*, x2 = x1; BOOST_TEST(test::equivalent(x2.hash_function(), hf1)); BOOST_TEST(test::equivalent(x2.key_eq(), eq1)); - BOOST_TEST(test::equivalent(x2.get_allocator(), al2)); + if (test::is_propagate_on_assign::value) { + BOOST_TEST(test::equivalent(x2.get_allocator(), al1)); + BOOST_TEST(!test::equivalent(x2.get_allocator(), al2)); + } + else { + BOOST_TEST(test::equivalent(x2.get_allocator(), al2)); + BOOST_TEST(!test::equivalent(x2.get_allocator(), al1)); + } test::check_container(x2, v1); } } @@ -110,16 +120,56 @@ boost::unordered_multimap >* test_multimap; +boost::unordered_set >* + test_set_prop_assign; +boost::unordered_multiset >* + test_multiset_prop_assign; +boost::unordered_map >* + test_map_prop_assign; +boost::unordered_multimap >* + test_multimap_prop_assign; + +boost::unordered_set >* + test_set_no_prop_assign; +boost::unordered_multiset >* + test_multiset_no_prop_assign; +boost::unordered_map >* + test_map_no_prop_assign; +boost::unordered_multimap >* + test_multimap_no_prop_assign; + using test::default_generator; using test::generate_collisions; -UNORDERED_TEST(assign_tests1, - ((test_set)(test_multiset)(test_map)(test_multimap)) +UNORDERED_TEST(assign_tests1, ( + (test_set)(test_multiset)(test_map)(test_multimap) + (test_set_prop_assign)(test_multiset_prop_assign)(test_map_prop_assign)(test_multimap_prop_assign) + (test_set_no_prop_assign)(test_multiset_no_prop_assign)(test_map_no_prop_assign)(test_multimap_no_prop_assign) + ) ((default_generator)(generate_collisions)) ) -UNORDERED_TEST(assign_tests2, - ((test_set)(test_multiset)(test_map)(test_multimap)) +UNORDERED_TEST(assign_tests2, ( + (test_set)(test_multiset)(test_map)(test_multimap) + (test_set_prop_assign)(test_multiset_prop_assign)(test_map_prop_assign)(test_multimap_prop_assign) + (test_set_no_prop_assign)(test_multiset_no_prop_assign)(test_map_no_prop_assign)(test_multimap_no_prop_assign) + ) ((default_generator)(generate_collisions)) ) diff --git a/test/unordered/move_tests.cpp b/test/unordered/move_tests.cpp index 6ac741c8..daf4ecf3 100644 --- a/test/unordered/move_tests.cpp +++ b/test/unordered/move_tests.cpp @@ -9,6 +9,7 @@ #include #include "../helpers/test.hpp" #include "../objects/test.hpp" +#include "../objects/cxx11_allocator.hpp" #include "../helpers/random_values.hpp" #include "../helpers/tracker.hpp" #include "../helpers/equivalent.hpp" @@ -140,7 +141,7 @@ namespace move_tests BOOST_TEST(y.max_load_factor() == 2.0); // Not necessarily required. test::check_equivalent_keys(y); } -/* + { test::check_instances check_; @@ -161,7 +162,57 @@ namespace move_tests BOOST_TEST(y.max_load_factor() == 1.0); // Not necessarily required. test::check_equivalent_keys(y); } -*/ } + } + + template + void move_assign_tests2(T*, + test::random_generator const& generator = test::default_generator) + { + BOOST_DEDUCED_TYPENAME T::hasher hf(1); + BOOST_DEDUCED_TYPENAME T::key_equal eq(1); + BOOST_DEDUCED_TYPENAME T::allocator_type al1(1); + BOOST_DEDUCED_TYPENAME T::allocator_type al2(2); + typedef BOOST_DEDUCED_TYPENAME T::allocator_type allocator_type; + + { + test::random_values v(500, generator); + test::random_values v2(0, generator); + T y(v.begin(), v.end(), 0, hf, eq, al1); + test::object_count count; + y = create(v2, count, hf, eq, al2, 2.0); + BOOST_TEST(y.empty()); + test::check_container(y, v2); + test::check_equivalent_keys(y); + BOOST_TEST(y.max_load_factor() == 2.0); + if (test::is_propagate_on_move::value) { + BOOST_TEST(test::equivalent(y.get_allocator(), al2)); + } + else { + BOOST_TEST(test::equivalent(y.get_allocator(), al1)); + } + } + + { + test::random_values v(500, generator); + test::object_count count; + T y(0, hf, eq, al1); + y = create(v, count, hf, eq, al2, 0.5); +#if defined(BOOST_HAS_NRVO) + if (test::is_propagate_on_move::value) { + BOOST_TEST(count == test::global_object_count); + } +#endif + test::check_container(y, v); + test::check_equivalent_keys(y); + BOOST_TEST(y.max_load_factor() == 0.5); + if (test::is_propagate_on_move::value) { + BOOST_TEST(test::equivalent(y.get_allocator(), al2)); + } + else { + BOOST_TEST(test::equivalent(y.get_allocator(), al1)); + } + } + } boost::unordered_set >* test_multimap; +boost::unordered_set >* + test_set_prop_move; +boost::unordered_multiset >* + test_multiset_prop_move; +boost::unordered_map >* + test_map_prop_move; +boost::unordered_multimap >* + test_multimap_prop_move; + +boost::unordered_set >* + test_set_no_prop_move; +boost::unordered_multiset >* + test_multiset_no_prop_move; +boost::unordered_map >* + test_map_no_prop_move; +boost::unordered_multimap >* + test_multimap_no_prop_move; + using test::default_generator; using test::generate_collisions; - UNORDERED_TEST(move_construct_tests1, - ((test_set)(test_multiset)(test_map)(test_multimap)) + UNORDERED_TEST(move_construct_tests1, ( + (test_set)(test_multiset)(test_map)(test_multimap) + (test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move) + (test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(test_multimap_no_prop_move) + ) ) - UNORDERED_TEST(move_assign_tests1, - ((test_set)(test_multiset)(test_map)(test_multimap)) + UNORDERED_TEST(move_assign_tests1, ( + (test_set)(test_multiset)(test_map)(test_multimap) + (test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move) + (test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(test_multimap_no_prop_move) + ) ) - UNORDERED_TEST(move_construct_tests2, - ((test_set)(test_multiset)(test_map)(test_multimap)) + UNORDERED_TEST(move_construct_tests2, ( + (test_set)(test_multiset)(test_map)(test_multimap) + (test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move) + (test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(test_multimap_no_prop_move) + ) ((default_generator)(generate_collisions)) ) + UNORDERED_TEST(move_assign_tests2, ( + (test_set)(test_multiset)(test_map)(test_multimap) + (test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move) + (test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(test_multimap_no_prop_move) + ) + ) } RUN_TESTS() diff --git a/test/unordered/swap_tests.cpp b/test/unordered/swap_tests.cpp index e09072cb..4ff5fda9 100644 --- a/test/unordered/swap_tests.cpp +++ b/test/unordered/swap_tests.cpp @@ -12,6 +12,7 @@ #include #include "../helpers/test.hpp" #include "../objects/test.hpp" +#include "../objects/cxx11_allocator.hpp" #include "../helpers/random_values.hpp" #include "../helpers/tracker.hpp" #include "../helpers/invariants.hpp" @@ -107,15 +108,24 @@ void swap_tests2(X* ptr = 0, } { + test::force_equal_allocator force_( + !test::is_propagate_on_swap::value); test::check_instances check_; test::random_values vx(50, generator), vy(100, generator); X x(vx.begin(), vx.end(), 0, hasher(), key_equal(), allocator_type(1)); X y(vy.begin(), vy.end(), 0, hasher(), key_equal(), allocator_type(2)); - swap_test_impl(x, y); + + if (test::is_propagate_on_swap::value || + x.get_allocator() == y.get_allocator()) + { + swap_test_impl(x, y); + } } { + test::force_equal_allocator force_( + !test::is_propagate_on_swap::value); test::check_instances check_; test::random_values vx(100, generator), vy(100, generator); @@ -123,31 +133,74 @@ void swap_tests2(X* ptr = 0, allocator_type(1)); X y(vy.begin(), vy.end(), 0, hasher(2), key_equal(2), allocator_type(2)); - swap_test_impl(x, y); - swap_test_impl(x, y); + + if (test::is_propagate_on_swap::value || + x.get_allocator() == y.get_allocator()) + { + swap_test_impl(x, y); + swap_test_impl(x, y); + } } } boost::unordered_set >* test_set; + test::hash, test::equal_to, + test::allocator >* test_set; boost::unordered_multiset >* test_multiset; + test::hash, test::equal_to, + test::allocator >* test_multiset; boost::unordered_map >* test_map; + test::hash, test::equal_to, + test::allocator >* test_map; boost::unordered_multimap >* test_multimap; + test::hash, test::equal_to, + test::allocator >* test_multimap; -UNORDERED_TEST(swap_tests1, - ((test_set)(test_multiset)(test_map)(test_multimap)) -) +boost::unordered_set >* + test_set_prop_swap; +boost::unordered_multiset >* + test_multiset_prop_swap; +boost::unordered_map >* + test_map_prop_swap; +boost::unordered_multimap >* + test_multimap_prop_swap; -UNORDERED_TEST(swap_tests2, - ((test_set)(test_multiset)(test_map)(test_multimap)) -) +boost::unordered_set >* + test_set_no_prop_swap; +boost::unordered_multiset >* + test_multiset_no_prop_swap; +boost::unordered_map >* + test_map_no_prop_swap; +boost::unordered_multimap >* + test_multimap_no_prop_swap; + +UNORDERED_TEST(swap_tests1, ( + (test_set)(test_multiset)(test_map)(test_multimap) + (test_set_prop_swap)(test_multiset_prop_swap)(test_map_prop_swap)(test_multimap_prop_swap) + (test_set_no_prop_swap)(test_multiset_no_prop_swap)(test_map_no_prop_swap)(test_multimap_no_prop_swap) +)) + +UNORDERED_TEST(swap_tests2, ( + (test_set)(test_multiset)(test_map)(test_multimap) + (test_set_prop_swap)(test_multiset_prop_swap)(test_map_prop_swap)(test_multimap_prop_swap) + (test_set_no_prop_swap)(test_multiset_no_prop_swap)(test_map_no_prop_swap)(test_multimap_no_prop_swap) +)) } RUN_TESTS()