From f64cf03e1d5eb3ce610f201107ab9c58b512f285 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 15 Aug 2011 07:48:53 +0000 Subject: [PATCH] Unordered: Implement select_on_container_copy_construction support. [SVN r73772] --- .../unordered/detail/allocator_helpers.hpp | 73 +++++++++- test/helpers/memory.hpp | 8 ++ test/objects/cxx11_allocator.hpp | 127 ++++++++++++++---- test/objects/test.hpp | 7 - test/unordered/copy_tests.cpp | 67 ++++++++- 5 files changed, 238 insertions(+), 44 deletions(-) diff --git a/include/boost/unordered/detail/allocator_helpers.hpp b/include/boost/unordered/detail/allocator_helpers.hpp index 728c7f0e..4a4dff76 100644 --- a/include/boost/unordered/detail/allocator_helpers.hpp +++ b/include/boost/unordered/detail/allocator_helpers.hpp @@ -17,6 +17,7 @@ #include #include +#include #if (defined(BOOST_NO_STD_ALLOCATOR) || defined(BOOST_DINKUMWARE_STDLIB)) \ && !defined(__BORLANDC__) @@ -125,6 +126,69 @@ namespace boost { namespace unordered { namespace detail { BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_move_assignment); BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_swap); +// Disabling for Visual C++ for now as it hasn't been tested yet. +#if !defined(BOOST_NO_SFINAE_EXPR) // || BOOST_WORKAROUND(BOOST_MSVC, >= 1400) + // Specialization is only needed for Visual C++. Without it SFINAE doesn't + // kick in. + template + struct expr_sfinae; + + template <> + struct expr_sfinae { + typedef yes_type type; + }; + + template + struct has_select_on_container_copy_construction + { + // This needs to be a template for Visual C++. + template + static yes_type to_yes_type(const T2&); + + template + static typename expr_sfinaeselect_on_container_copy_construction() + ))>::type check(T2*); + + static no_type check(void*); + + enum { value = sizeof(check((T*) 0)) == sizeof(yes_type) }; + }; +#else + template + struct has_select_on_container_copy_construction + { + typedef T (T::*SelectFunc)() const; + + template struct sfinae { typedef yes_type type; }; + + template + static typename sfinae<&U::select_on_container_copy_construction>::type + test(int); + template + static no_type test(...); + + enum { value = sizeof(test(1)) == sizeof(yes_type) }; + }; + +#endif + + template + inline BOOST_DEDUCED_TYPENAME boost::enable_if< + has_select_on_container_copy_construction, Alloc + >::type call_select_on_container_copy_construction(const Alloc& rhs) + { + return rhs.select_on_container_copy_construction(); + } + + template + inline BOOST_DEDUCED_TYPENAME boost::disable_if< + has_select_on_container_copy_construction, Alloc + >::type call_select_on_container_copy_construction(const Alloc& rhs) + { + return rhs; + } + template struct allocator_traits { @@ -197,10 +261,11 @@ namespace boost { namespace unordered { namespace detail { { return a.max_size(); } // Allocator propagation on construction - - static Alloc select_on_container_copy_construction(const Alloc& rhs) { - //return BOOST_DEFAULT_FUNC(select_on_container_copy_construction,Alloc)(rhs); - return rhs; + + static Alloc select_on_container_copy_construction(Alloc const& rhs) + { + return boost::unordered::detail:: + call_select_on_container_copy_construction(rhs); } // Allocator propagation on assignment and swap. diff --git a/test/helpers/memory.hpp b/test/helpers/memory.hpp index 71245c0b..7ed3d046 100644 --- a/test/helpers/memory.hpp +++ b/test/helpers/memory.hpp @@ -194,12 +194,20 @@ namespace test enum { value = false }; }; + template + struct is_select_on_copy : false_type {}; 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 + int selected_count(Alloc const&) + { + return 0; + } } #endif diff --git a/test/objects/cxx11_allocator.hpp b/test/objects/cxx11_allocator.hpp index 61f24e49..2971da44 100644 --- a/test/objects/cxx11_allocator.hpp +++ b/test/objects/cxx11_allocator.hpp @@ -10,6 +10,7 @@ #include #include +#include "../helpers/fwd.hpp" #include "../helpers/memory.hpp" namespace test @@ -28,15 +29,6 @@ namespace test 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 { @@ -76,14 +68,11 @@ namespace test { 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 + template + struct cxx11_allocator_base { int tag_; + int selected_; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; @@ -93,29 +82,26 @@ namespace test typedef T const& const_reference; typedef T value_type; - template struct rebind { - typedef cxx11_allocator other; - }; - - explicit cxx11_allocator(int t = 0) : tag_(t) + explicit cxx11_allocator_base(int t) + : tag_(t), selected_(0) { detail::tracker.allocator_ref(); } - template cxx11_allocator( - cxx11_allocator const& x) - : tag_(x.tag_) + template cxx11_allocator_base( + cxx11_allocator_base const& x) + : tag_(x.tag_), selected_(x.selected_) { detail::tracker.allocator_ref(); } - cxx11_allocator(cxx11_allocator const& x) - : tag_(x.tag_) + cxx11_allocator_base(cxx11_allocator_base const& x) + : tag_(x.tag_), selected_(x.selected_) { detail::tracker.allocator_ref(); } - ~cxx11_allocator() + ~cxx11_allocator_base() { detail::tracker.allocator_unref(); } @@ -149,7 +135,7 @@ namespace test // Note that tags will be tested // properly in the normal allocator. detail::tracker.track_deallocate((void*) p, n, sizeof(T), tag_, - (Flags & propagate_swap) ? true : false); + !force_equal_allocator_value); ::operator delete((void*) p); } @@ -173,12 +159,88 @@ namespace test size_type max_size() const { return (std::numeric_limits::max)(); } + }; + + template + struct cxx11_allocator : + public cxx11_allocator_base, + public swap_allocator_base, + public assign_allocator_base, + public move_allocator_base + { + template struct rebind { + typedef cxx11_allocator other; + }; + + explicit cxx11_allocator(int t = 0) + : cxx11_allocator_base(t) + { + } + + template cxx11_allocator( + cxx11_allocator const& x) + : cxx11_allocator_base(x) + { + } + + cxx11_allocator(cxx11_allocator const& x) + : cxx11_allocator_base(x) + { + } // 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_); + return force_equal_allocator_value || (this->tag_ == x.tag_); + } + + bool operator!=(cxx11_allocator const& x) const + { + return !(*this == x); + } + }; + + template + struct cxx11_allocator : + public cxx11_allocator_base, + public swap_allocator_base, + public assign_allocator_base, + public move_allocator_base + { + cxx11_allocator select_on_container_copy_construction() const + { + cxx11_allocator tmp(*this); + ++tmp.selected_; + return tmp; + } + + template struct rebind { + typedef cxx11_allocator other; + }; + + explicit cxx11_allocator(int t = 0) + : cxx11_allocator_base(t) + { + } + + template cxx11_allocator( + cxx11_allocator const& x) + : cxx11_allocator_base(x) + { + } + + cxx11_allocator(cxx11_allocator const& x) + : cxx11_allocator_base(x) + { + } + + // When not propagating swap, allocators are always equal + // to avoid undefined behaviour. + bool operator==(cxx11_allocator const& x) const + { + return force_equal_allocator_value || (this->tag_ == x.tag_); } bool operator!=(cxx11_allocator const& x) const @@ -196,6 +258,9 @@ namespace test return x.tag_ == y.tag_; } + template + struct is_select_on_copy > + : bool_type<(Flags & select_copy) ? true : false> {}; template struct is_propagate_on_swap > : bool_type<(Flags & propagate_swap) ? true : false> {}; @@ -205,6 +270,12 @@ namespace test template struct is_propagate_on_move > : bool_type<(Flags & propagate_move) ? true : false> {}; + + template + int selected_count(cxx11_allocator const& x) + { + return x.selected_; + } } #endif diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 57dec792..a2cf6c87 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -286,13 +286,6 @@ namespace test } }; - 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/copy_tests.cpp b/test/unordered/copy_tests.cpp index f6a70179..e0d4950e 100644 --- a/test/unordered/copy_tests.cpp +++ b/test/unordered/copy_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" @@ -23,9 +24,11 @@ template void copy_construct_tests1(T*, test::random_generator const& generator = test::default_generator) { + typedef BOOST_DEDUCED_TYPENAME T::allocator_type allocator_type; + BOOST_DEDUCED_TYPENAME T::hasher hf; BOOST_DEDUCED_TYPENAME T::key_equal eq; - BOOST_DEDUCED_TYPENAME T::allocator_type al; + BOOST_DEDUCED_TYPENAME T::allocator_type al; { test::check_instances check_; @@ -37,6 +40,8 @@ void copy_construct_tests1(T*, BOOST_TEST(test::equivalent(y.key_eq(), eq)); BOOST_TEST(test::equivalent(y.get_allocator(), al)); BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + BOOST_TEST(test::selected_count(y.get_allocator()) == + (test::is_select_on_copy::value ? 1 : 0)); test::check_equivalent_keys(y); } @@ -49,6 +54,8 @@ void copy_construct_tests1(T*, T y(x); test::unordered_equivalence_tester equivalent(x); BOOST_TEST(equivalent(y)); + BOOST_TEST(test::selected_count(y.get_allocator()) == + (test::is_select_on_copy::value ? 1 : 0)); test::check_equivalent_keys(y); } @@ -67,6 +74,8 @@ void copy_construct_tests1(T*, BOOST_TEST(equivalent(y)); // This isn't guaranteed: BOOST_TEST(y.load_factor() < y.max_load_factor()); + BOOST_TEST(test::selected_count(y.get_allocator()) == + (test::is_select_on_copy::value ? 1 : 0)); test::check_equivalent_keys(y); } } @@ -81,6 +90,8 @@ void copy_construct_tests2(T* ptr, BOOST_DEDUCED_TYPENAME T::key_equal eq(1); BOOST_DEDUCED_TYPENAME T::allocator_type al(1); BOOST_DEDUCED_TYPENAME T::allocator_type al2(2); + + typedef BOOST_DEDUCED_TYPENAME T::allocator_type allocator_type; { test::check_instances check_; @@ -92,6 +103,8 @@ void copy_construct_tests2(T* ptr, BOOST_TEST(test::equivalent(y.key_eq(), eq)); BOOST_TEST(test::equivalent(y.get_allocator(), al)); BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + BOOST_TEST(test::selected_count(y.get_allocator()) == + (test::is_select_on_copy::value ? 1 : 0)); test::check_equivalent_keys(y); } @@ -105,6 +118,7 @@ void copy_construct_tests2(T* ptr, BOOST_TEST(test::equivalent(y.key_eq(), eq)); BOOST_TEST(test::equivalent(y.get_allocator(), al2)); BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + BOOST_TEST(test::selected_count(y.get_allocator()) == 0); test::check_equivalent_keys(y); } @@ -118,6 +132,8 @@ void copy_construct_tests2(T* ptr, test::unordered_equivalence_tester equivalent(x); BOOST_TEST(equivalent(y)); test::check_equivalent_keys(y); + BOOST_TEST(test::selected_count(y.get_allocator()) == + (test::is_select_on_copy::value ? 1 : 0)); BOOST_TEST(test::equivalent(y.get_allocator(), al)); } @@ -131,6 +147,7 @@ void copy_construct_tests2(T* ptr, test::unordered_equivalence_tester equivalent(x); BOOST_TEST(equivalent(y)); test::check_equivalent_keys(y); + BOOST_TEST(test::selected_count(y.get_allocator()) == 0); BOOST_TEST(test::equivalent(y.get_allocator(), al2)); } } @@ -148,15 +165,55 @@ boost::unordered_multimap >* test_multimap; +boost::unordered_set >* + test_set_select_copy; +boost::unordered_multiset >* + test_multiset_select_copy; +boost::unordered_map >* + test_map_select_copy; +boost::unordered_multimap >* + test_multimap_select_copy; + +boost::unordered_set >* + test_set_no_select_copy; +boost::unordered_multiset >* + test_multiset_no_select_copy; +boost::unordered_map >* + test_map_no_select_copy; +boost::unordered_multimap >* + test_multimap_no_select_copy; + using test::default_generator; using test::generate_collisions; -UNORDERED_TEST(copy_construct_tests1, - ((test_set)(test_multiset)(test_map)(test_multimap)) +UNORDERED_TEST(copy_construct_tests1, ( + (test_set)(test_multiset)(test_map)(test_multimap) + (test_set_select_copy)(test_multiset_select_copy)(test_map_select_copy)(test_multimap_select_copy) + (test_set_no_select_copy)(test_multiset_no_select_copy)(test_map_no_select_copy)(test_multimap_no_select_copy) + ) ) -UNORDERED_TEST(copy_construct_tests2, - ((test_set)(test_multiset)(test_map)(test_multimap)) +UNORDERED_TEST(copy_construct_tests2, ( + (test_set)(test_multiset)(test_map)(test_multimap) + (test_set_select_copy)(test_multiset_select_copy)(test_map_select_copy)(test_multimap_select_copy) + (test_set_no_select_copy)(test_multiset_no_select_copy)(test_map_no_select_copy)(test_multimap_no_select_copy) + ) ((default_generator)(generate_collisions)) )