From 7e5520f974bc74078ed680af0c7d1a8d852370b4 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 01/16] Increase template depth for GCC This appears to be an unavoidable problem with GCC's tuple implementation. For example: http://stackoverflow.com/q/23374953/2434 Appears to be okay in later versions of GCC though. --- test/Jamfile.v2 | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index c50aec60..72c09867 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -15,6 +15,7 @@ project unordered-test/unordered darwin:"-pedantic -Wstrict-aliasing -fstrict-aliasing -Wextra -Wsign-promo -Wunused-parameter -Wconversion -Wfloat-equal -Wshadow" clang:"-pedantic -Wstrict-aliasing -fstrict-aliasing -Wextra -Wsign-promo -Wunused-parameter -Wsign-conversion -Wconversion -Wfloat-equal -Wshadow" msvc:"/wd4494" + gcc:500 ; #alias framework : /boost/test//boost_unit_test_framework ; From 1092c972c908861599c30840dbb754888a70d198 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 02/16] Use allocator_traits to construct/destruct buckets --- .../boost/unordered/detail/implementation.hpp | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 47bc5b82..c534a94e 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -2156,6 +2156,7 @@ template struct bucket link_pointer next_; bucket() : next_() {} + bucket(link_pointer n) : next_(n) {} link_pointer first_from_start() { return next_; } @@ -2171,6 +2172,7 @@ struct ptr_bucket link_pointer next_; ptr_bucket() : next_(0) {} + ptr_bucket(link_pointer n) : next_(n) {} link_pointer first_from_start() { return this; } @@ -2826,30 +2828,39 @@ struct table : boost::unordered::detail::functions(length); + new_buckets + static_cast(new_count); for (; constructed != end; ++constructed) { - new ((void*)boost::addressof(*constructed)) bucket(); + boost::unordered::detail::func::call_construct( + bucket_alloc(), boost::addressof(*constructed)); } if (buckets_) { // Copy the nodes to the new buckets, including the dummy // node if there is one. - (new_buckets + static_cast(new_count))->next_ = + boost::unordered::detail::func::call_construct(bucket_alloc(), + boost::addressof(*constructed), (buckets_ + static_cast(bucket_count_)) - ->next_; + ->next_); + ++constructed; destroy_buckets(); } else if (bucket::extra_node) { node_constructor a(node_alloc()); a.create_node(); - (new_buckets + static_cast(new_count))->next_ = - a.release(); + boost::unordered::detail::func::call_construct(bucket_alloc(), + boost::addressof(*constructed), a.release()); + ++constructed; + } else { + boost::unordered::detail::func::call_construct( + bucket_alloc(), boost::addressof(*constructed)); + ++constructed; } } BOOST_CATCH(...) { for (bucket_pointer p = new_buckets; p != constructed; ++p) { - boost::unordered::detail::func::destroy(boost::addressof(*p)); + boost::unordered::detail::func::call_destroy( + bucket_alloc(), boost::addressof(*p)); } bucket_allocator_traits::deallocate( @@ -2984,7 +2995,8 @@ struct table : boost::unordered::detail::functions Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 03/16] Replace BOOST_UNORDERED_DETAIL_FULL_CONSTRUCT with BOOST_UNORDERED_CXX11_CONSTRUCTION Require good construct support and piecewise construction. I don't know if there are any platforms with good construct support, but no piecewise construction, if there are then they'll no longer use 'allocator_traits::construct'/'allocator_traits::destruct'. --- .../boost/unordered/detail/implementation.hpp | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index c534a94e..c0f069c8 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -104,6 +104,25 @@ #define BOOST_UNORDERED_USE_ALLOCATOR_TRAITS 0 #endif +// BOOST_UNORDERED_CXX11_CONSTRUCTION +// +// Use C++11 construction, requires variadic arguments, good construct support +// in allocator_traits and piecewise construction of std::pair +// Otherwise allocators aren't used for construction/destruction + +#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT && \ + !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +#if BOOST_UNORDERED_USE_ALLOCATOR_TRAITS == 0 && !defined(BOOST_NO_SFINAE_EXPR) +#define BOOST_UNORDERED_CXX11_CONSTRUCTION 1 +#elif BOOST_UNORDERED_USE_ALLOCATOR_TRAITS == 1 +#define BOOST_UNORDERED_CXX11_CONSTRUCTION 1 +#endif +#endif + +#if !defined(BOOST_UNORDERED_CXX11_CONSTRUCTION) +#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0 +#endif + // // Other configuration macros // @@ -765,13 +784,6 @@ template struct identity #include #include -#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && \ - !defined(BOOST_NO_SFINAE_EXPR) -#define BOOST_UNORDERED_DETAIL_FULL_CONSTRUCT 1 -#else -#define BOOST_UNORDERED_DETAIL_FULL_CONSTRUCT 0 -#endif - namespace boost { namespace unordered { namespace detail { @@ -1040,7 +1052,7 @@ template struct allocator_traits } public: -#if BOOST_UNORDERED_DETAIL_FULL_CONSTRUCT +#if BOOST_UNORDERED_CXX11_CONSTRUCTION template static typename boost::enable_if_c< @@ -1195,8 +1207,6 @@ template struct allocator_traits #include -#define BOOST_UNORDERED_DETAIL_FULL_CONSTRUCT 1 - namespace boost { namespace unordered { namespace detail { @@ -1222,8 +1232,6 @@ template struct rebind_wrap #include -#define BOOST_UNORDERED_DETAIL_FULL_CONSTRUCT 0 - namespace boost { namespace unordered { namespace detail { @@ -1260,9 +1268,7 @@ namespace func { //////////////////////////////////////////////////////////////////////////// // call_construct -#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) - -#if BOOST_UNORDERED_DETAIL_FULL_CONSTRUCT +#if BOOST_UNORDERED_CXX11_CONSTRUCTION template inline void call_construct( @@ -1278,7 +1284,7 @@ inline void call_destroy(Alloc& alloc, T* x) boost::unordered::detail::allocator_traits::destroy(alloc, x); } -#else +#elif !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) template inline void call_construct(Alloc&, T* address, BOOST_FWD_REF(Args)... args) @@ -1291,9 +1297,8 @@ template inline void call_destroy(Alloc&, T* x) boost::unordered::detail::func::destroy(x); } -#endif - #else + template inline void call_construct(Alloc&, T* address) { From bc36a06a2da557fda977677070a1e26919c9aaf2 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 04/16] Comment about call_construct --- include/boost/unordered/detail/implementation.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index c0f069c8..dac24837 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1267,6 +1267,9 @@ namespace func { //////////////////////////////////////////////////////////////////////////// // call_construct +// +// Only use allocator_traits::construct, allocator_traits::destroy when full +// C++11 support is available. #if BOOST_UNORDERED_CXX11_CONSTRUCTION From c333a7f9fcab6382b787dfed0deaaf81c1ed0aa4 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 05/16] Use piecewise construction where possible --- .../boost/unordered/detail/implementation.hpp | 118 ++++++++++++++++-- 1 file changed, 106 insertions(+), 12 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index dac24837..1df07137 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1324,7 +1324,7 @@ template inline void call_destroy(Alloc&, T* x) //////////////////////////////////////////////////////////////////////////// // Construct from tuple // -// Used for piecewise construction. +// Used to emulate piecewise construction. #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) @@ -1408,7 +1408,8 @@ template struct length BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(10, boost::) -#if !defined(__SUNPRO_CC) && !defined(BOOST_NO_CXX11_HDR_TUPLE) +#if !BOOST_UNORDERED_CXX11_CONSTRUCTION && !defined(__SUNPRO_CC) && \ + !defined(BOOST_NO_CXX11_HDR_TUPLE) BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(10, std::) #endif @@ -1444,13 +1445,11 @@ template struct use_piecewise }; }; -#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +#if BOOST_UNORDERED_CXX11_CONSTRUCTION //////////////////////////////////////////////////////////////////////////// // Construct from variadic parameters -// For the standard pair constructor. - template inline void construct_from_args( Alloc& alloc, T* address, BOOST_FWD_REF(Args)... args) @@ -1459,10 +1458,64 @@ inline void construct_from_args( alloc, address, boost::forward(args)...); } -// Special case for piece_construct -// -// TODO: When possible, it might be better to use std::pair's -// constructor for std::piece_construct with std::tuple. +// For backwards compatibility, implement a special case for +// piecewise_construct with boost::tuple + +template struct detect_boost_tuple +{ + template + static choice1::type test(choice1, boost::tuple const&); + + static choice2::type test(choice2, ...); + + enum + { + value = sizeof(choice1::type) == + sizeof(test(choose(), boost::unordered::detail::make())) + }; +}; + +// Special case for piecewise_construct + +template +inline typename boost::enable_if_c::value && + detect_boost_tuple::value && + detect_boost_tuple::value, + void>::type +construct_from_args(Alloc& alloc, std::pair* address, BOOST_FWD_REF(A0), + BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) +{ + boost::unordered::detail::func::construct_from_tuple( + alloc, boost::addressof(address->first), boost::forward(a1)); + BOOST_TRY + { + boost::unordered::detail::func::construct_from_tuple( + alloc, boost::addressof(address->second), boost::forward(a2)); + } + BOOST_CATCH(...) + { + boost::unordered::detail::func::destroy( + boost::addressof(address->first)); + BOOST_RETHROW; + } + BOOST_CATCH_END +} + +#elif !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + +//////////////////////////////////////////////////////////////////////////// +// Construct from variadic parameters + +template +inline void construct_from_args( + Alloc& alloc, T* address, BOOST_FWD_REF(Args)... args) +{ + boost::unordered::detail::func::call_construct( + alloc, address, boost::forward(args)...); +} + +// Special case for piecewise_construct template @@ -1540,7 +1593,7 @@ BOOST_PP_REPEAT_FROM_TO( #undef BOOST_UNORDERED_CONSTRUCT_IMPL -// Construct with piece_construct +// Construct with piecewise_construct template @@ -1716,8 +1769,47 @@ construct_node(Alloc& alloc, BOOST_FWD_REF(U) x) return a.release(); } -// TODO: When possible, it might be better to use std::pair's -// constructor for std::piece_construct with std::tuple. +#if BOOST_UNORDERED_CXX11_CONSTRUCTION + +template +inline typename boost::unordered::detail::allocator_traits::pointer +construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k) +{ + node_constructor a(alloc); + a.create_node(); + boost::unordered::detail::func::call_construct(alloc, a.node_->value_ptr(), + std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), + std::forward_as_tuple()); + return a.release(); +} + +template +inline typename boost::unordered::detail::allocator_traits::pointer +construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(Mapped) m) +{ + node_constructor a(alloc); + a.create_node(); + boost::unordered::detail::func::call_construct(alloc, a.node_->value_ptr(), + std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), + std::forward_as_tuple(boost::forward(m))); + return a.release(); +} + +template +inline typename boost::unordered::detail::allocator_traits::pointer +construct_node_pair_from_args( + Alloc& alloc, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(Args)... args) +{ + node_constructor a(alloc); + a.create_node(); + boost::unordered::detail::func::call_construct(alloc, a.node_->value_ptr(), + std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), + std::forward_as_tuple(boost::forward(args)...)); + return a.release(); +} + +#else + template inline typename boost::unordered::detail::allocator_traits::pointer construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k) @@ -1801,6 +1893,8 @@ construct_node_pair_from_args( BOOST_CATCH_END return a.release(); } + +#endif } } } From 7de8c91301728e95515f2cd0d8c6411a619cf0cb Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 06/16] Remove calls to const_cast_pointer It was needed because std::allocator_traits::construct doesn't work with a const pointer (e.g. pointer to the first member of a std::pair). But now we're only calling construct if BOOST_UNORDERED_CXX11_CONSTRUCTION is true, so the allocator_traits::construct is no longer used here. --- .../boost/unordered/detail/implementation.hpp | 73 +++++-------------- 1 file changed, 20 insertions(+), 53 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 1df07137..a10e4c79 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -485,17 +485,6 @@ struct convert_from_anything template convert_from_anything(T const&); }; -namespace func { -// This is a bit nasty, when constructing the individual members -// of a std::pair, need to cast away 'const'. For modern compilers, -// should be able to use std::piecewise_construct instead. -template T* const_cast_pointer(T* x) { return x; } -template T* const_cast_pointer(T const* x) -{ - return const_cast(x); -} -} - //////////////////////////////////////////////////////////////////////////// // emplace_args // @@ -1524,21 +1513,16 @@ inline typename enable_if, void>::type construct_from_args( BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) { boost::unordered::detail::func::construct_from_tuple( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(address->first)), - boost::forward(a1)); + alloc, boost::addressof(address->first), boost::forward(a1)); BOOST_TRY { boost::unordered::detail::func::construct_from_tuple( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(address->second)), - boost::forward(a2)); + alloc, boost::addressof(address->second), boost::forward(a2)); } BOOST_CATCH(...) { boost::unordered::detail::func::call_destroy( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(address->first))); + alloc, boost::addressof(address->first)); BOOST_RETHROW; } BOOST_CATCH_END @@ -1602,21 +1586,16 @@ inline void construct_from_args(Alloc& alloc, std::pair* address, typename enable_if, void*>::type = 0) { boost::unordered::detail::func::construct_from_tuple( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(address->first)), - args.a1); + alloc, boost::addressof(address->first), args.a1); BOOST_TRY { boost::unordered::detail::func::construct_from_tuple( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(address->second)), - args.a2); + alloc, boost::addressof(address->second), args.a2); } BOOST_CATCH(...) { boost::unordered::detail::func::call_destroy( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(address->first))); + alloc, boost::addressof(address->first)); BOOST_RETHROW; } BOOST_CATCH_END @@ -1816,21 +1795,17 @@ construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k) { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::func::call_construct( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(a.node_->value_ptr()->first)), - boost::forward(k)); + boost::unordered::detail::func::call_construct(alloc, + boost::addressof(a.node_->value_ptr()->first), boost::forward(k)); BOOST_TRY { boost::unordered::detail::func::call_construct( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(a.node_->value_ptr()->second))); + alloc, boost::addressof(a.node_->value_ptr()->second)); } BOOST_CATCH(...) { boost::unordered::detail::func::call_destroy( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(a.node_->value_ptr()->first))); + alloc, boost::addressof(a.node_->value_ptr()->first)); BOOST_RETHROW; } BOOST_CATCH_END @@ -1843,22 +1818,18 @@ construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(Mapped) m) { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::func::call_construct( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(a.node_->value_ptr()->first)), - boost::forward(k)); + boost::unordered::detail::func::call_construct(alloc, + boost::addressof(a.node_->value_ptr()->first), boost::forward(k)); BOOST_TRY { - boost::unordered::detail::func::call_construct( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(a.node_->value_ptr()->second)), + boost::unordered::detail::func::call_construct(alloc, + boost::addressof(a.node_->value_ptr()->second), boost::forward(m)); } BOOST_CATCH(...) { boost::unordered::detail::func::call_destroy( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(a.node_->value_ptr()->first))); + alloc, boost::addressof(a.node_->value_ptr()->first)); BOOST_RETHROW; } BOOST_CATCH_END @@ -1872,22 +1843,18 @@ construct_node_pair_from_args( { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::func::call_construct( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(a.node_->value_ptr()->first)), - boost::forward(k)); + boost::unordered::detail::func::call_construct(alloc, + boost::addressof(a.node_->value_ptr()->first), boost::forward(k)); BOOST_TRY { - boost::unordered::detail::func::construct_from_args( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(a.node_->value_ptr()->second)), + boost::unordered::detail::func::construct_from_args(alloc, + boost::addressof(a.node_->value_ptr()->second), BOOST_UNORDERED_EMPLACE_FORWARD); } BOOST_CATCH(...) { boost::unordered::detail::func::call_destroy( - alloc, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(a.node_->value_ptr()->first))); + alloc, boost::addressof(a.node_->value_ptr()->first)); BOOST_RETHROW; } BOOST_CATCH_END From 9e7068004463d7dbce2b20a158a5e4f9cb3f65d6 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 07/16] Bypass construct_value/call_destroy_in a few places --- .../boost/unordered/detail/implementation.hpp | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index a10e4c79..38eaa6a5 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1319,9 +1319,9 @@ template inline void call_destroy(Alloc&, T* x) #define BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(n, namespace_) \ template \ - void construct_from_tuple(Alloc& alloc, T* ptr, namespace_ tuple<>) \ + void construct_from_tuple(Alloc&, T* ptr, namespace_ tuple<>) \ { \ - boost::unordered::detail::func::call_construct(alloc, ptr); \ + new ((void*)ptr) T(); \ } \ \ BOOST_PP_REPEAT_FROM_TO( \ @@ -1330,10 +1330,10 @@ template inline void call_destroy(Alloc&, T* x) #define BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE_IMPL(z, n, namespace_) \ template \ - void construct_from_tuple(Alloc& alloc, T* ptr, \ + void construct_from_tuple(Alloc&, T* ptr, \ namespace_ tuple const& x) \ { \ - boost::unordered::detail::func::call_construct(alloc, ptr, \ + new ((void*)ptr) T( \ BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_GET_TUPLE_ARG, namespace_)); \ } @@ -1443,7 +1443,7 @@ template inline void construct_from_args( Alloc& alloc, T* address, BOOST_FWD_REF(Args)... args) { - boost::unordered::detail::func::call_construct( + boost::unordered::detail::allocator_traits::construct( alloc, address, boost::forward(args)...); } @@ -1500,8 +1500,7 @@ template inline void construct_from_args( Alloc& alloc, T* address, BOOST_FWD_REF(Args)... args) { - boost::unordered::detail::func::call_construct( - alloc, address, boost::forward(args)...); + new ((void*)address) T(boost::forward(args)...); } // Special case for piecewise_construct @@ -1521,8 +1520,8 @@ inline typename enable_if, void>::type construct_from_args( } BOOST_CATCH(...) { - boost::unordered::detail::func::call_destroy( - alloc, boost::addressof(address->first)); + boost::unordered::detail::func::destroy( + boost::addressof(address->first)); BOOST_RETHROW; } BOOST_CATCH_END @@ -1594,8 +1593,8 @@ inline void construct_from_args(Alloc& alloc, std::pair* address, } BOOST_CATCH(...) { - boost::unordered::detail::func::call_destroy( - alloc, boost::addressof(address->first)); + boost::unordered::detail::func::destroy( + boost::addressof(address->first)); BOOST_RETHROW; } BOOST_CATCH_END @@ -1756,9 +1755,9 @@ construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k) { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::func::call_construct(alloc, a.node_->value_ptr(), - std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), - std::forward_as_tuple()); + boost::unordered::detail::allocator_traits::construct(alloc, + a.node_->value_ptr(), std::piecewise_construct, + std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple()); return a.release(); } @@ -1768,8 +1767,9 @@ construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(Mapped) m) { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::func::call_construct(alloc, a.node_->value_ptr(), - std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), + boost::unordered::detail::allocator_traits::construct(alloc, + a.node_->value_ptr(), std::piecewise_construct, + std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple(boost::forward(m))); return a.release(); } @@ -1781,8 +1781,9 @@ construct_node_pair_from_args( { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::func::call_construct(alloc, a.node_->value_ptr(), - std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), + boost::unordered::detail::allocator_traits::construct(alloc, + a.node_->value_ptr(), std::piecewise_construct, + std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple(boost::forward(args)...)); return a.release(); } @@ -1804,8 +1805,8 @@ construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k) } BOOST_CATCH(...) { - boost::unordered::detail::func::call_destroy( - alloc, boost::addressof(a.node_->value_ptr()->first)); + boost::unordered::detail::func::destroy( + boost::addressof(a.node_->value_ptr()->first)); BOOST_RETHROW; } BOOST_CATCH_END @@ -1828,8 +1829,8 @@ construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(Mapped) m) } BOOST_CATCH(...) { - boost::unordered::detail::func::call_destroy( - alloc, boost::addressof(a.node_->value_ptr()->first)); + boost::unordered::detail::func::destroy( + boost::addressof(a.node_->value_ptr()->first)); BOOST_RETHROW; } BOOST_CATCH_END @@ -1853,8 +1854,8 @@ construct_node_pair_from_args( } BOOST_CATCH(...) { - boost::unordered::detail::func::call_destroy( - alloc, boost::addressof(a.node_->value_ptr()->first)); + boost::unordered::detail::func::destroy( + boost::addressof(a.node_->value_ptr()->first)); BOOST_RETHROW; } BOOST_CATCH_END From 6d79a322e2e184bb7ec758f80c403d62f24cfb37 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 08/16] Use macros to reduce call chain --- .../boost/unordered/detail/implementation.hpp | 89 +++++++++---------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 38eaa6a5..09baab65 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1262,19 +1262,11 @@ namespace func { #if BOOST_UNORDERED_CXX11_CONSTRUCTION -template -inline void call_construct( - Alloc& alloc, T* address, BOOST_FWD_REF(Args)... args) -{ - boost::unordered::detail::allocator_traits::construct( - alloc, address, boost::forward(args)...); -} - -template -inline void call_destroy(Alloc& alloc, T* x) -{ - boost::unordered::detail::allocator_traits::destroy(alloc, x); -} +#define BOOST_UNORDERED_CALL_CONSTRUCT0(Traits, alloc, address) \ + Traits::construct(alloc, address) +#define BOOST_UNORDERED_CALL_CONSTRUCT1(Traits, alloc, address, a0) \ + Traits::construct(alloc, address, a0) +#define BOOST_UNORDERED_CALL_DESTROY(Traits, alloc, x) Traits::destroy(alloc, x) #elif !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) @@ -1284,10 +1276,12 @@ inline void call_construct(Alloc&, T* address, BOOST_FWD_REF(Args)... args) new ((void*)address) T(boost::forward(args)...); } -template inline void call_destroy(Alloc&, T* x) -{ - boost::unordered::detail::func::destroy(x); -} +#define BOOST_UNORDERED_CALL_CONSTRUCT0(Traits, alloc, address) \ + boost::unordered::detail::func::call_construct(alloc, address) +#define BOOST_UNORDERED_CALL_CONSTRUCT1(Traits, alloc, address, a0) \ + boost::unordered::detail::func::call_construct(alloc, address, a0) +#define BOOST_UNORDERED_CALL_DESTROY(Traits, alloc, x) \ + boost::unordered::detail::func::destroy(x) #else @@ -1303,10 +1297,12 @@ inline void call_construct(Alloc&, T* address, BOOST_FWD_REF(A0) a0) new ((void*)address) T(boost::forward(a0)); } -template inline void call_destroy(Alloc&, T* x) -{ - boost::unordered::detail::func::destroy(x); -} +#define BOOST_UNORDERED_CALL_CONSTRUCT0(Traits, alloc, address) \ + boost::unordered::detail::func::call_construct(alloc, address) +#define BOOST_UNORDERED_CALL_CONSTRUCT1(Traits, alloc, address, a0) \ + boost::unordered::detail::func::call_construct(alloc, address, a0) +#define BOOST_UNORDERED_CALL_DESTROY(Traits, alloc, x) \ + boost::unordered::detail::func::destroy(x) #endif @@ -1650,8 +1646,8 @@ template struct node_constructor BOOST_ASSERT(!node_); node_ = p; node_constructed_ = true; - boost::unordered::detail::func::call_destroy( - alloc_, node_->value_ptr()); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, alloc_, node_->value_ptr()); } private: @@ -1707,8 +1703,8 @@ template struct node_tmp template node_tmp::~node_tmp() { if (node_) { - boost::unordered::detail::func::call_destroy( - alloc_, node_->value_ptr()); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, alloc_, node_->value_ptr()); boost::unordered::detail::func::destroy(boost::addressof(*node_)); node_allocator_traits::deallocate(alloc_, node_, 1); } @@ -1742,8 +1738,9 @@ construct_node(Alloc& alloc, BOOST_FWD_REF(U) x) { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::func::call_construct( - alloc, a.node_->value_ptr(), boost::forward(x)); + BOOST_UNORDERED_CALL_CONSTRUCT1( + boost::unordered::detail::allocator_traits, alloc, + a.node_->value_ptr(), boost::forward(x)); return a.release(); } @@ -2183,7 +2180,7 @@ template struct node_holder } else { constructor_.create_node(); } - boost::unordered::detail::func::call_construct( + BOOST_UNORDERED_CALL_CONSTRUCT1(node_allocator_traits, constructor_.alloc_, constructor_.node_->value_ptr(), v); return constructor_.release(); } @@ -2195,8 +2192,9 @@ template struct node_holder } else { constructor_.create_node(); } - boost::unordered::detail::func::call_construct(constructor_.alloc_, - constructor_.node_->value_ptr(), boost::move(v)); + BOOST_UNORDERED_CALL_CONSTRUCT1(node_allocator_traits, + constructor_.alloc_, constructor_.node_->value_ptr(), + boost::move(v)); return constructor_.release(); } @@ -2209,8 +2207,8 @@ template node_holder::~node_holder() node_pointer p = nodes_; nodes_ = static_cast(p->next_); - boost::unordered::detail::func::call_destroy( - constructor_.alloc_, p->value_ptr()); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, constructor_.alloc_, p->value_ptr()); boost::unordered::detail::func::destroy(boost::addressof(*p)); node_allocator_traits::deallocate(constructor_.alloc_, p, 1); } @@ -2900,15 +2898,15 @@ struct table : boost::unordered::detail::functions(new_count); for (; constructed != end; ++constructed) { - boost::unordered::detail::func::call_construct( + BOOST_UNORDERED_CALL_CONSTRUCT0(bucket_allocator_traits, bucket_alloc(), boost::addressof(*constructed)); } if (buckets_) { // Copy the nodes to the new buckets, including the dummy // node if there is one. - boost::unordered::detail::func::call_construct(bucket_alloc(), - boost::addressof(*constructed), + BOOST_UNORDERED_CALL_CONSTRUCT1(bucket_allocator_traits, + bucket_alloc(), boost::addressof(*constructed), (buckets_ + static_cast(bucket_count_)) ->next_); ++constructed; @@ -2917,11 +2915,12 @@ struct table : boost::unordered::detail::functions(prev->next_); prev->next_ = n->next_; - boost::unordered::detail::func::call_destroy( - node_alloc(), n->value_ptr()); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, node_alloc(), n->value_ptr()); boost::unordered::detail::func::destroy(boost::addressof(*n)); node_allocator_traits::deallocate(node_alloc(), n, 1); --size_; @@ -3065,8 +3064,8 @@ struct table : boost::unordered::detail::functions if (!a.node_) { a.create_node(); } - boost::unordered::detail::func::call_construct( - a.alloc_, a.node_->value_ptr(), *i); + BOOST_UNORDERED_CALL_CONSTRUCT1( + node_allocator_traits, a.alloc_, a.node_->value_ptr(), *i); node_tmp b(a.release(), a.alloc_); const_key_type& k = this->get_key(b.node_); From 08ce2c98e0bdec3b62d6beb08393fdc40b5aafce Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 09/16] Rename call_construct to construct_value --- .../boost/unordered/detail/implementation.hpp | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 09baab65..e49fe4d7 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1255,7 +1255,7 @@ namespace detail { namespace func { //////////////////////////////////////////////////////////////////////////// -// call_construct +// construct_value // // Only use allocator_traits::construct, allocator_traits::destroy when full // C++11 support is available. @@ -1271,36 +1271,36 @@ namespace func { #elif !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) template -inline void call_construct(Alloc&, T* address, BOOST_FWD_REF(Args)... args) +inline void construct_value(Alloc&, T* address, BOOST_FWD_REF(Args)... args) { new ((void*)address) T(boost::forward(args)...); } #define BOOST_UNORDERED_CALL_CONSTRUCT0(Traits, alloc, address) \ - boost::unordered::detail::func::call_construct(alloc, address) + boost::unordered::detail::func::construct_value(alloc, address) #define BOOST_UNORDERED_CALL_CONSTRUCT1(Traits, alloc, address, a0) \ - boost::unordered::detail::func::call_construct(alloc, address, a0) + boost::unordered::detail::func::construct_value(alloc, address, a0) #define BOOST_UNORDERED_CALL_DESTROY(Traits, alloc, x) \ boost::unordered::detail::func::destroy(x) #else template -inline void call_construct(Alloc&, T* address) +inline void construct_value(Alloc&, T* address) { new ((void*)address) T(); } template -inline void call_construct(Alloc&, T* address, BOOST_FWD_REF(A0) a0) +inline void construct_value(Alloc&, T* address, BOOST_FWD_REF(A0) a0) { new ((void*)address) T(boost::forward(a0)); } #define BOOST_UNORDERED_CALL_CONSTRUCT0(Traits, alloc, address) \ - boost::unordered::detail::func::call_construct(alloc, address) + boost::unordered::detail::func::construct_value(alloc, address) #define BOOST_UNORDERED_CALL_CONSTRUCT1(Traits, alloc, address, a0) \ - boost::unordered::detail::func::call_construct(alloc, address, a0) + boost::unordered::detail::func::construct_value(alloc, address, a0) #define BOOST_UNORDERED_CALL_DESTROY(Traits, alloc, x) \ boost::unordered::detail::func::destroy(x) @@ -1793,11 +1793,11 @@ construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k) { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::func::call_construct(alloc, + boost::unordered::detail::func::construct_value(alloc, boost::addressof(a.node_->value_ptr()->first), boost::forward(k)); BOOST_TRY { - boost::unordered::detail::func::call_construct( + boost::unordered::detail::func::construct_value( alloc, boost::addressof(a.node_->value_ptr()->second)); } BOOST_CATCH(...) @@ -1816,11 +1816,11 @@ construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(Mapped) m) { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::func::call_construct(alloc, + boost::unordered::detail::func::construct_value(alloc, boost::addressof(a.node_->value_ptr()->first), boost::forward(k)); BOOST_TRY { - boost::unordered::detail::func::call_construct(alloc, + boost::unordered::detail::func::construct_value(alloc, boost::addressof(a.node_->value_ptr()->second), boost::forward(m)); } @@ -1841,7 +1841,7 @@ construct_node_pair_from_args( { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::func::call_construct(alloc, + boost::unordered::detail::func::construct_value(alloc, boost::addressof(a.node_->value_ptr()->first), boost::forward(k)); BOOST_TRY { From 3414e6628add93a2bef3c647dbb7c4d73c3d0f7e Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 10/16] Use allocator to construct/destroy nodes --- .../boost/unordered/detail/implementation.hpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index e49fe4d7..4741170a 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1659,7 +1659,8 @@ template node_constructor::~node_constructor() { if (node_) { if (node_constructed_) { - boost::unordered::detail::func::destroy(boost::addressof(*node_)); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, alloc_, boost::addressof(*node_)); } node_allocator_traits::deallocate(alloc_, node_, 1); @@ -1673,7 +1674,8 @@ template void node_constructor::create_node() node_ = node_allocator_traits::allocate(alloc_, 1); - new ((void*)boost::addressof(*node_)) node(); + BOOST_UNORDERED_CALL_CONSTRUCT0( + node_allocator_traits, alloc_, boost::addressof(*node_)); node_->init(node_); node_constructed_ = true; } @@ -1705,7 +1707,8 @@ template node_tmp::~node_tmp() if (node_) { BOOST_UNORDERED_CALL_DESTROY( node_allocator_traits, alloc_, node_->value_ptr()); - boost::unordered::detail::func::destroy(boost::addressof(*node_)); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, alloc_, boost::addressof(*node_)); node_allocator_traits::deallocate(alloc_, node_, 1); } } @@ -2209,7 +2212,8 @@ template node_holder::~node_holder() BOOST_UNORDERED_CALL_DESTROY( node_allocator_traits, constructor_.alloc_, p->value_ptr()); - boost::unordered::detail::func::destroy(boost::addressof(*p)); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, constructor_.alloc_, boost::addressof(*p)); node_allocator_traits::deallocate(constructor_.alloc_, p, 1); } } @@ -3009,7 +3013,8 @@ struct table : boost::unordered::detail::functionsvalue_ptr()); - boost::unordered::detail::func::destroy(boost::addressof(*n)); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, node_alloc(), boost::addressof(*n)); node_allocator_traits::deallocate(node_alloc(), n, 1); --size_; } @@ -3037,7 +3042,8 @@ struct table : boost::unordered::detail::functions(get_bucket(bucket_count_)->next_); - boost::unordered::detail::func::destroy(boost::addressof(*n)); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, node_alloc(), boost::addressof(*n)); node_allocator_traits::deallocate(node_alloc(), n, 1); } From e0054c7dd0dae53e57ae3414e6c786d3798b6ac6 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 11/16] Remove alloc parameter from construct_value --- .../boost/unordered/detail/implementation.hpp | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 4741170a..95502fe0 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1270,37 +1270,36 @@ namespace func { #elif !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) -template -inline void construct_value(Alloc&, T* address, BOOST_FWD_REF(Args)... args) +template +inline void construct_value(T* address, BOOST_FWD_REF(Args)... args) { new ((void*)address) T(boost::forward(args)...); } #define BOOST_UNORDERED_CALL_CONSTRUCT0(Traits, alloc, address) \ - boost::unordered::detail::func::construct_value(alloc, address) + boost::unordered::detail::func::construct_value(address) #define BOOST_UNORDERED_CALL_CONSTRUCT1(Traits, alloc, address, a0) \ - boost::unordered::detail::func::construct_value(alloc, address, a0) + boost::unordered::detail::func::construct_value(address, a0) #define BOOST_UNORDERED_CALL_DESTROY(Traits, alloc, x) \ boost::unordered::detail::func::destroy(x) #else -template -inline void construct_value(Alloc&, T* address) +template inline void construct_value(T* address) { new ((void*)address) T(); } -template -inline void construct_value(Alloc&, T* address, BOOST_FWD_REF(A0) a0) +template +inline void construct_value(T* address, BOOST_FWD_REF(A0) a0) { new ((void*)address) T(boost::forward(a0)); } #define BOOST_UNORDERED_CALL_CONSTRUCT0(Traits, alloc, address) \ - boost::unordered::detail::func::construct_value(alloc, address) + boost::unordered::detail::func::construct_value(address) #define BOOST_UNORDERED_CALL_CONSTRUCT1(Traits, alloc, address, a0) \ - boost::unordered::detail::func::construct_value(alloc, address, a0) + boost::unordered::detail::func::construct_value(address, a0) #define BOOST_UNORDERED_CALL_DESTROY(Traits, alloc, x) \ boost::unordered::detail::func::destroy(x) @@ -1796,12 +1795,12 @@ construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k) { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::func::construct_value(alloc, + boost::unordered::detail::func::construct_value( boost::addressof(a.node_->value_ptr()->first), boost::forward(k)); BOOST_TRY { boost::unordered::detail::func::construct_value( - alloc, boost::addressof(a.node_->value_ptr()->second)); + boost::addressof(a.node_->value_ptr()->second)); } BOOST_CATCH(...) { @@ -1819,11 +1818,11 @@ construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(Mapped) m) { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::func::construct_value(alloc, + boost::unordered::detail::func::construct_value( boost::addressof(a.node_->value_ptr()->first), boost::forward(k)); BOOST_TRY { - boost::unordered::detail::func::construct_value(alloc, + boost::unordered::detail::func::construct_value( boost::addressof(a.node_->value_ptr()->second), boost::forward(m)); } @@ -1844,7 +1843,7 @@ construct_node_pair_from_args( { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::func::construct_value(alloc, + boost::unordered::detail::func::construct_value( boost::addressof(a.node_->value_ptr()->first), boost::forward(k)); BOOST_TRY { From cafd236a1880f2efcb27d8845142e1afa68cf6e5 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 12/16] Test more memory tracking --- test/helpers/memory.hpp | 2 + test/objects/test.hpp | 13 ++- test/unordered/insert_tests.cpp | 150 ++++++++++++++++++++++---------- 3 files changed, 118 insertions(+), 47 deletions(-) diff --git a/test/helpers/memory.hpp b/test/helpers/memory.hpp index 84822b4a..16837e77 100644 --- a/test/helpers/memory.hpp +++ b/test/helpers/memory.hpp @@ -66,6 +66,8 @@ struct memory_tracker { } + ~memory_tracker() { BOOST_TEST(count_allocators == 0); } + void allocator_ref() { if (count_allocators == 0) { diff --git a/test/objects/test.hpp b/test/objects/test.hpp index ab346806..aba215f0 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -391,16 +391,23 @@ template class allocator1 ::operator delete((void*)p); } +#if BOOST_UNORDERED_CXX11_CONSTRUCTION + template void construct(T* p, Args&&... args) + { + detail::tracker.track_construct((void*)p, sizeof(T), tag_); + new (p) T(boost::forward(args)...); + } +#else void construct(T* p, T const& t) { - // Don't count constructions here as it isn't always called. - // detail::tracker.track_construct((void*) p, sizeof(T), tag_); + detail::tracker.track_construct((void*)p, sizeof(T), tag_); new (p) T(t); } +#endif void destroy(T* p) { - // detail::tracker.track_destroy((void*) p, sizeof(T), tag_); + detail::tracker.track_destroy((void*)p, sizeof(T), tag_); p->~T(); // Work around MSVC buggy unused parameter warning. diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index ae50fa18..332a9b0f 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -1030,21 +1030,43 @@ struct overloaded_constructor UNORDERED_AUTO_TEST(map_emplace_test) { - boost::unordered_map x; + { + boost::unordered_map > > + x; #if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100)) - x.emplace(); - BOOST_TEST( - x.find(0) != x.end() && x.find(0)->second == overloaded_constructor()); + x.emplace(); + BOOST_TEST(x.find(0) != x.end() && + x.find(0)->second == overloaded_constructor()); #endif - x.emplace(2, 3); - BOOST_TEST( - x.find(2) != x.end() && x.find(2)->second == overloaded_constructor(3)); + x.emplace(2, 3); + BOOST_TEST(x.find(2) != x.end() && + x.find(2)->second == overloaded_constructor(3)); - x.try_emplace(5); - BOOST_TEST( - x.find(5) != x.end() && x.find(5)->second == overloaded_constructor()); + x.try_emplace(5); + BOOST_TEST(x.find(5) != x.end() && + x.find(5)->second == overloaded_constructor()); + } + + { + boost::unordered_multimap > > + x; + +#if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100)) + x.emplace(); + BOOST_TEST(x.find(0) != x.end() && + x.find(0)->second == overloaded_constructor()); +#endif + + x.emplace(2, 3); + BOOST_TEST(x.find(2) != x.end() && + x.find(2)->second == overloaded_constructor(3)); + } } UNORDERED_AUTO_TEST(set_emplace_test) @@ -1098,48 +1120,88 @@ struct convertible_to_piecewise UNORDERED_AUTO_TEST(map_emplace_test2) { - boost::unordered_map x; + { + boost::unordered_map, + std::equal_to, + test::allocator1 > > + x; - x.emplace(boost::unordered::piecewise_construct, boost::make_tuple(), - boost::make_tuple()); - BOOST_TEST( - x.find(overloaded_constructor()) != x.end() && - x.find(overloaded_constructor())->second == overloaded_constructor()); + x.emplace(boost::unordered::piecewise_construct, boost::make_tuple(), + boost::make_tuple()); + BOOST_TEST(x.find(overloaded_constructor()) != x.end() && + x.find(overloaded_constructor())->second == + overloaded_constructor()); - x.emplace( - convertible_to_piecewise(), boost::make_tuple(1), boost::make_tuple()); - BOOST_TEST( - x.find(overloaded_constructor(1)) != x.end() && - x.find(overloaded_constructor(1))->second == overloaded_constructor()); + x.emplace(convertible_to_piecewise(), boost::make_tuple(1), + boost::make_tuple()); + BOOST_TEST(x.find(overloaded_constructor(1)) != x.end() && + x.find(overloaded_constructor(1))->second == + overloaded_constructor()); - x.emplace(piecewise_rvalue(), boost::make_tuple(2, 3), - boost::make_tuple(4, 5, 6)); - BOOST_TEST(x.find(overloaded_constructor(2, 3)) != x.end() && - x.find(overloaded_constructor(2, 3))->second == - overloaded_constructor(4, 5, 6)); + x.emplace(piecewise_rvalue(), boost::make_tuple(2, 3), + boost::make_tuple(4, 5, 6)); + BOOST_TEST(x.find(overloaded_constructor(2, 3)) != x.end() && + x.find(overloaded_constructor(2, 3))->second == + overloaded_constructor(4, 5, 6)); - derived_from_piecewise_construct_t d; - x.emplace(d, boost::make_tuple(9, 3, 1), boost::make_tuple(10)); - BOOST_TEST(x.find(overloaded_constructor(9, 3, 1)) != x.end() && - x.find(overloaded_constructor(9, 3, 1))->second == - overloaded_constructor(10)); + derived_from_piecewise_construct_t d; + x.emplace(d, boost::make_tuple(9, 3, 1), boost::make_tuple(10)); + BOOST_TEST(x.find(overloaded_constructor(9, 3, 1)) != x.end() && + x.find(overloaded_constructor(9, 3, 1))->second == + overloaded_constructor(10)); - x.clear(); + x.clear(); - x.try_emplace(overloaded_constructor()); - BOOST_TEST( - x.find(overloaded_constructor()) != x.end() && - x.find(overloaded_constructor())->second == overloaded_constructor()); + x.try_emplace(overloaded_constructor()); + BOOST_TEST(x.find(overloaded_constructor()) != x.end() && + x.find(overloaded_constructor())->second == + overloaded_constructor()); - x.try_emplace(1); - BOOST_TEST( - x.find(overloaded_constructor(1)) != x.end() && - x.find(overloaded_constructor(1))->second == overloaded_constructor()); + x.try_emplace(1); + BOOST_TEST(x.find(overloaded_constructor(1)) != x.end() && + x.find(overloaded_constructor(1))->second == + overloaded_constructor()); - x.try_emplace(overloaded_constructor(2, 3), 4, 5, 6); - BOOST_TEST(x.find(overloaded_constructor(2, 3)) != x.end() && - x.find(overloaded_constructor(2, 3))->second == - overloaded_constructor(4, 5, 6)); + x.try_emplace(overloaded_constructor(2, 3), 4, 5, 6); + BOOST_TEST(x.find(overloaded_constructor(2, 3)) != x.end() && + x.find(overloaded_constructor(2, 3))->second == + overloaded_constructor(4, 5, 6)); + } + { + + boost::unordered_multimap, + std::equal_to, + test::allocator1 > > + x; + + x.emplace(boost::unordered::piecewise_construct, boost::make_tuple(), + boost::make_tuple()); + BOOST_TEST(x.find(overloaded_constructor()) != x.end() && + x.find(overloaded_constructor())->second == + overloaded_constructor()); + + x.emplace(convertible_to_piecewise(), boost::make_tuple(1), + boost::make_tuple()); + BOOST_TEST(x.find(overloaded_constructor(1)) != x.end() && + x.find(overloaded_constructor(1))->second == + overloaded_constructor()); + + x.emplace(piecewise_rvalue(), boost::make_tuple(2, 3), + boost::make_tuple(4, 5, 6)); + BOOST_TEST(x.find(overloaded_constructor(2, 3)) != x.end() && + x.find(overloaded_constructor(2, 3))->second == + overloaded_constructor(4, 5, 6)); + + derived_from_piecewise_construct_t d; + x.emplace(d, boost::make_tuple(9, 3, 1), boost::make_tuple(10)); + BOOST_TEST(x.find(overloaded_constructor(9, 3, 1)) != x.end() && + x.find(overloaded_constructor(9, 3, 1))->second == + overloaded_constructor(10)); + } } UNORDERED_AUTO_TEST(set_emplace_test2) From 7a0a598649131aaec54616dc8736b5cdcab498e6 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 13/16] Don't track construction when using boost::tuple Because it doesn't quiet work on C++11 compilers onwards. --- test/helpers/memory.hpp | 39 +++++++++-- test/objects/test.hpp | 12 ++-- test/unordered/insert_tests.cpp | 114 +++++++++++++++++++++++++++++++- 3 files changed, 152 insertions(+), 13 deletions(-) diff --git a/test/helpers/memory.hpp b/test/helpers/memory.hpp index 16837e77..2f910676 100644 --- a/test/helpers/memory.hpp +++ b/test/helpers/memory.hpp @@ -60,9 +60,11 @@ struct memory_tracker unsigned int count_allocators; unsigned int count_allocations; unsigned int count_constructions; + bool tracking_constructions; memory_tracker() - : count_allocators(0), count_allocations(0), count_constructions(0) + : count_allocators(0), count_allocations(0), count_constructions(0), + tracking_constructions(true) { } @@ -133,14 +135,18 @@ struct memory_tracker void track_construct(void* /*ptr*/, std::size_t /*size*/, int /*tag*/) { - ++count_constructions; + if (tracking_constructions) { + ++count_constructions; + } } void track_destroy(void* /*ptr*/, std::size_t /*size*/, int /*tag*/) { - BOOST_TEST(count_constructions > 0); - if (count_constructions > 0) - --count_constructions; + if (tracking_constructions) { + BOOST_TEST(count_constructions > 0); + if (count_constructions > 0) + --count_constructions; + } } }; } @@ -155,6 +161,29 @@ namespace { test::detail::memory_tracker tracker; } } + +namespace detail { +struct disable_construction_tracking +{ + bool old_value; + + disable_construction_tracking() + : old_value(detail::tracker.tracking_constructions) + { + test::detail::tracker.tracking_constructions = false; + } + + ~disable_construction_tracking() + { + test::detail::tracker.tracking_constructions = old_value; + } + + private: + disable_construction_tracking(disable_construction_tracking const&); + disable_construction_tracking& operator=( + disable_construction_tracking const&); +}; +} } #endif diff --git a/test/objects/test.hpp b/test/objects/test.hpp index aba215f0..14f29ab9 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -392,10 +392,10 @@ template class allocator1 } #if BOOST_UNORDERED_CXX11_CONSTRUCTION - template void construct(T* p, Args&&... args) + template void construct(U* p, Args&&... args) { - detail::tracker.track_construct((void*)p, sizeof(T), tag_); - new (p) T(boost::forward(args)...); + detail::tracker.track_construct((void*)p, sizeof(U), tag_); + new (p) U(boost::forward(args)...); } #else void construct(T* p, T const& t) @@ -405,10 +405,10 @@ template class allocator1 } #endif - void destroy(T* p) + template void destroy(U* p) { - detail::tracker.track_destroy((void*)p, sizeof(T), tag_); - p->~T(); + detail::tracker.track_destroy((void*)p, sizeof(U), tag_); + p->~U(); // Work around MSVC buggy unused parameter warning. ignore_variable(&p); diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index 332a9b0f..a53f43c3 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -1120,6 +1120,10 @@ struct convertible_to_piecewise UNORDERED_AUTO_TEST(map_emplace_test2) { + // Emulating piecewise construction with boost::tuple bypasses the + // allocator's construct method, but still uses test destroy method. + test::detail::disable_construction_tracking _scoped; + { boost::unordered_map, @@ -1169,8 +1173,8 @@ UNORDERED_AUTO_TEST(map_emplace_test2) x.find(overloaded_constructor(2, 3))->second == overloaded_constructor(4, 5, 6)); } - { + { boost::unordered_multimap, std::equal_to, @@ -1220,9 +1224,115 @@ UNORDERED_AUTO_TEST(set_emplace_test2) boost::make_tuple(2, 3)); check = std::make_pair(overloaded_constructor(1), overloaded_constructor(2, 3)); - ; BOOST_TEST(x.find(check) != x.end() && *x.find(check) == check); } + +#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT + +UNORDERED_AUTO_TEST(map_std_emplace_test2) +{ + { + boost::unordered_map, + std::equal_to, + test::allocator1 > > + x; + + x.emplace( + std::piecewise_construct, std::make_tuple(), std::make_tuple()); + BOOST_TEST(x.find(overloaded_constructor()) != x.end() && + x.find(overloaded_constructor())->second == + overloaded_constructor()); + + x.emplace( + convertible_to_piecewise(), std::make_tuple(1), std::make_tuple()); + BOOST_TEST(x.find(overloaded_constructor(1)) != x.end() && + x.find(overloaded_constructor(1))->second == + overloaded_constructor()); + + x.emplace(piecewise_rvalue(), std::make_tuple(2, 3), + std::make_tuple(4, 5, 6)); + BOOST_TEST(x.find(overloaded_constructor(2, 3)) != x.end() && + x.find(overloaded_constructor(2, 3))->second == + overloaded_constructor(4, 5, 6)); + + derived_from_piecewise_construct_t d; + x.emplace(d, std::make_tuple(9, 3, 1), std::make_tuple(10)); + BOOST_TEST(x.find(overloaded_constructor(9, 3, 1)) != x.end() && + x.find(overloaded_constructor(9, 3, 1))->second == + overloaded_constructor(10)); + + x.clear(); + + x.try_emplace(overloaded_constructor()); + BOOST_TEST(x.find(overloaded_constructor()) != x.end() && + x.find(overloaded_constructor())->second == + overloaded_constructor()); + + x.try_emplace(1); + BOOST_TEST(x.find(overloaded_constructor(1)) != x.end() && + x.find(overloaded_constructor(1))->second == + overloaded_constructor()); + + x.try_emplace(overloaded_constructor(2, 3), 4, 5, 6); + BOOST_TEST(x.find(overloaded_constructor(2, 3)) != x.end() && + x.find(overloaded_constructor(2, 3))->second == + overloaded_constructor(4, 5, 6)); + } + { + boost::unordered_multimap, + std::equal_to, + test::allocator1 > > + x; + + x.emplace( + std::piecewise_construct, std::make_tuple(), std::make_tuple()); + BOOST_TEST(x.find(overloaded_constructor()) != x.end() && + x.find(overloaded_constructor())->second == + overloaded_constructor()); + + x.emplace( + convertible_to_piecewise(), std::make_tuple(1), std::make_tuple()); + BOOST_TEST(x.find(overloaded_constructor(1)) != x.end() && + x.find(overloaded_constructor(1))->second == + overloaded_constructor()); + + x.emplace(piecewise_rvalue(), std::make_tuple(2, 3), + std::make_tuple(4, 5, 6)); + BOOST_TEST(x.find(overloaded_constructor(2, 3)) != x.end() && + x.find(overloaded_constructor(2, 3))->second == + overloaded_constructor(4, 5, 6)); + + derived_from_piecewise_construct_t d; + x.emplace(d, std::make_tuple(9, 3, 1), std::make_tuple(10)); + BOOST_TEST(x.find(overloaded_constructor(9, 3, 1)) != x.end() && + x.find(overloaded_constructor(9, 3, 1))->second == + overloaded_constructor(10)); + } +} + +UNORDERED_AUTO_TEST(set_std_emplace_test2) +{ + boost::unordered_set< + std::pair > + x; + std::pair check; + + x.emplace(std::piecewise_construct, std::make_tuple(), std::make_tuple()); + BOOST_TEST(x.find(check) != x.end() && *x.find(check) == check); + + x.clear(); + x.emplace( + std::piecewise_construct, std::make_tuple(1), std::make_tuple(2, 3)); + check = + std::make_pair(overloaded_constructor(1), overloaded_constructor(2, 3)); + BOOST_TEST(x.find(check) != x.end() && *x.find(check) == check); +} + +#endif } RUN_TESTS() From ef05493c839bee3d072d23e9cf409d17b4ca602e Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 14/16] Test that construct/destroy aren't used when C++11 isn't available --- test/objects/test.hpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 14f29ab9..e0e155f9 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -397,13 +397,6 @@ template class allocator1 detail::tracker.track_construct((void*)p, sizeof(U), tag_); new (p) U(boost::forward(args)...); } -#else - void construct(T* p, T const& t) - { - detail::tracker.track_construct((void*)p, sizeof(T), tag_); - new (p) T(t); - } -#endif template void destroy(U* p) { @@ -413,6 +406,22 @@ template class allocator1 // Work around MSVC buggy unused parameter warning. ignore_variable(&p); } +#else + private: + // I'm going to claim in the documentation that construct/destroy + // is never used when C++11 support isn't available, so might as + // well check that in the text. + // TODO: Or maybe just disallow them for values? + template void construct(U* p); + template void construct(U* p, A0 const&); + template + void construct(U* p, A0 const&, A1 const&); + template + void construct(U* p, A0 const&, A1 const&, A2 const&); + template void destroy(U* p); + + public: +#endif bool operator==(allocator1 const& x) const { return tag_ == x.tag_; } From 8c9080f11f15d509439f7cc1d9ee69b15f5bf5c7 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 15/16] Document changes to allocator use --- doc/compliance.qbk | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/doc/compliance.qbk b/doc/compliance.qbk index 771e473d..b0fd9472 100644 --- a/doc/compliance.qbk +++ b/doc/compliance.qbk @@ -51,23 +51,32 @@ Due to imperfect move emulation, some assignments might check `propagate_on_container_copy_assignment` on some compilers and `propagate_on_container_move_assignment` on others. -The use of the allocator's construct and destruct methods might be a bit -surprising. -Nodes are constructed and destructed using the allocator, but the elements -are stored in aligned space within the node and constructed and destructed -by calling the constructor and destructor directly. +[endsect] -In C++11 the allocator's construct function has the signature: +[section:construction Construction/Destruction using allocators] - template - void construct(U* p, Args&&... args); +The following support is required for full use of C++11 style +construction/destruction: -which supports calling `construct` for the contained object, but -most existing allocators don't support this. If member function detection -was good enough then with old allocators it would fall back to calling -the element's constructor directly but in general, detection isn't good -enough to do this which is why Boost.Unordered just calls the constructor -directly every time. In most cases this will work okay. + * Variadic templates. + * Piecewise construction of `std::pair`. + * Either `std::allocator_traits` or expression SFINAE. + +This is detected using Boost.Config. The macro +`BOOST_UNORDERED_CXX11_CONSTRUCTION` will be set to 1 if it is found, or 0 +otherwise. + +When this is the case `allocator_traits::construct` and +`allocator_traits::destroy` will always be used, apart from when piecewise +constructing a `std::pair` using `boost::tuple` (see [link +unordered.compliance.pairs below]), but that should be easily avoided. + +When support is not available `allocator_traits::construct` and +`allocator_traits::destroy` are never called. + +[endsect] + +[section:pointer_traits Pointer Traits] `pointer_traits` aren't used. Instead, pointer types are obtained from rebound allocators, this can cause problems if the allocator can't be @@ -77,6 +86,7 @@ is used to obtain a const pointer. [endsect] +[#unordered.compliance.pairs] [section:pairs Pairs] Since the containers use `std::pair` they're limited to the version From 2effcfa1959b0cfd69ed71b6e8dffae3aa3c8ae9 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 18 Apr 2017 10:14:26 +0100 Subject: [PATCH 16/16] Intro to compliance section Needs more info on C++17 compliance - particularly `noexecpt`. --- doc/compliance.qbk | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/compliance.qbk b/doc/compliance.qbk index b0fd9472..1553d987 100644 --- a/doc/compliance.qbk +++ b/doc/compliance.qbk @@ -2,7 +2,13 @@ / 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) ] -[section:compliance C++11 Compliance] +[section:compliance Standard Compliance] + +The intent of Boost.Unordered is to implement a close (but inperfect) +implementation of the C++17 standard, that will work with C++98 upwards. +The wide compatibility does mean some comprimises have to be made. +With a compiler and library that fully support C++11, the differences should +be minor. [section:move Move emulation]