diff --git a/doc/compliance.qbk b/doc/compliance.qbk index 771e473d..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] @@ -51,23 +57,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 +92,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 diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 47bc5b82..95502fe0 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 // @@ -466,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 // @@ -765,13 +773,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 +1041,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 +1196,6 @@ template struct allocator_traits #include -#define BOOST_UNORDERED_DETAIL_FULL_CONSTRUCT 1 - namespace boost { namespace unordered { namespace detail { @@ -1222,8 +1221,6 @@ template struct rebind_wrap #include -#define BOOST_UNORDERED_DETAIL_FULL_CONSTRUCT 0 - namespace boost { namespace unordered { namespace detail { @@ -1258,73 +1255,68 @@ namespace detail { namespace func { //////////////////////////////////////////////////////////////////////////// -// call_construct +// construct_value +// +// Only use allocator_traits::construct, allocator_traits::destroy when full +// C++11 support is available. -#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +#if BOOST_UNORDERED_CXX11_CONSTRUCTION -#if BOOST_UNORDERED_DETAIL_FULL_CONSTRUCT +#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) -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)...); -} +#elif !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) -template -inline void call_destroy(Alloc& alloc, T* x) -{ - boost::unordered::detail::allocator_traits::destroy(alloc, x); -} - -#else - -template -inline void call_construct(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)...); } -template inline void call_destroy(Alloc&, T* x) -{ - boost::unordered::detail::func::destroy(x); -} - -#endif +#define BOOST_UNORDERED_CALL_CONSTRUCT0(Traits, alloc, address) \ + boost::unordered::detail::func::construct_value(address) +#define BOOST_UNORDERED_CALL_CONSTRUCT1(Traits, 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 call_construct(Alloc&, T* address) + +template inline void construct_value(T* address) { new ((void*)address) T(); } -template -inline void call_construct(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)); } -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::construct_value(address) +#define BOOST_UNORDERED_CALL_CONSTRUCT1(Traits, 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) #endif //////////////////////////////////////////////////////////////////////////// // Construct from tuple // -// Used for piecewise construction. +// Used to emulate piecewise construction. #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) #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( \ @@ -1333,10 +1325,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_)); \ } @@ -1400,7 +1392,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 @@ -1436,25 +1429,76 @@ 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) { - boost::unordered::detail::func::call_construct( + boost::unordered::detail::allocator_traits::construct( 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) +{ + new ((void*)address) T(boost::forward(args)...); +} + +// Special case for piecewise_construct template @@ -1463,21 +1507,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))); + boost::unordered::detail::func::destroy( + boost::addressof(address->first)); BOOST_RETHROW; } BOOST_CATCH_END @@ -1532,7 +1571,7 @@ BOOST_PP_REPEAT_FROM_TO( #undef BOOST_UNORDERED_CONSTRUCT_IMPL -// Construct with piece_construct +// Construct with piecewise_construct template @@ -1541,21 +1580,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))); + boost::unordered::detail::func::destroy( + boost::addressof(address->first)); BOOST_RETHROW; } BOOST_CATCH_END @@ -1611,8 +1645,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: @@ -1624,7 +1658,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); @@ -1638,7 +1673,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; } @@ -1668,9 +1704,10 @@ template struct node_tmp template node_tmp::~node_tmp() { if (node_) { - boost::unordered::detail::func::call_destroy( - alloc_, node_->value_ptr()); - boost::unordered::detail::func::destroy(boost::addressof(*node_)); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, alloc_, node_->value_ptr()); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, alloc_, boost::addressof(*node_)); node_allocator_traits::deallocate(alloc_, node_, 1); } } @@ -1703,34 +1740,72 @@ 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(); } -// 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, boost::unordered::detail::func::const_cast_pointer( - boost::addressof(a.node_->value_ptr()->first)), - 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()); + 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::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(); +} + +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::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(); +} + +#else + +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::construct_value( + 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::construct_value( + 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))); + boost::unordered::detail::func::destroy( + boost::addressof(a.node_->value_ptr()->first)); BOOST_RETHROW; } BOOST_CATCH_END @@ -1743,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::construct_value( + 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::construct_value( + 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))); + boost::unordered::detail::func::destroy( + boost::addressof(a.node_->value_ptr()->first)); BOOST_RETHROW; } BOOST_CATCH_END @@ -1772,27 +1843,25 @@ 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::construct_value( + 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))); + boost::unordered::detail::func::destroy( + boost::addressof(a.node_->value_ptr()->first)); BOOST_RETHROW; } BOOST_CATCH_END return a.release(); } + +#endif } } } @@ -2113,7 +2182,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(); } @@ -2125,8 +2194,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(); } @@ -2139,9 +2209,10 @@ 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::detail::func::destroy(boost::addressof(*p)); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, constructor_.alloc_, p->value_ptr()); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, constructor_.alloc_, boost::addressof(*p)); node_allocator_traits::deallocate(constructor_.alloc_, p, 1); } } @@ -2156,6 +2227,7 @@ template struct bucket link_pointer next_; bucket() : next_() {} + bucket(link_pointer n) : next_(n) {} link_pointer first_from_start() { return next_; } @@ -2171,6 +2243,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 +2899,40 @@ 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_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. - (new_buckets + static_cast(new_count))->next_ = + BOOST_UNORDERED_CALL_CONSTRUCT1(bucket_allocator_traits, + 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_CALL_CONSTRUCT1(bucket_allocator_traits, + bucket_alloc(), boost::addressof(*constructed), + a.release()); + ++constructed; + } else { + BOOST_UNORDERED_CALL_CONSTRUCT0(bucket_allocator_traits, + 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_CALL_DESTROY(bucket_allocator_traits, + bucket_alloc(), boost::addressof(*p)); } bucket_allocator_traits::deallocate( @@ -2927,9 +3010,10 @@ 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::detail::func::destroy(boost::addressof(*n)); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, node_alloc(), n->value_ptr()); + BOOST_UNORDERED_CALL_DESTROY( + node_allocator_traits, node_alloc(), boost::addressof(*n)); node_allocator_traits::deallocate(node_alloc(), n, 1); --size_; } @@ -2957,7 +3041,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); } @@ -2984,7 +3069,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_); 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 ; diff --git a/test/helpers/memory.hpp b/test/helpers/memory.hpp index 84822b4a..2f910676 100644 --- a/test/helpers/memory.hpp +++ b/test/helpers/memory.hpp @@ -60,12 +60,16 @@ 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) { } + ~memory_tracker() { BOOST_TEST(count_allocators == 0); } + void allocator_ref() { if (count_allocators == 0) { @@ -131,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; + } } }; } @@ -153,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 ab346806..e0e155f9 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -391,21 +391,37 @@ template class allocator1 ::operator delete((void*)p); } - void construct(T* p, T const& t) +#if BOOST_UNORDERED_CXX11_CONSTRUCTION + template void construct(U* p, Args&&... args) { - // Don't count constructions here as it isn't always called. - // detail::tracker.track_construct((void*) p, sizeof(T), tag_); - new (p) T(t); + detail::tracker.track_construct((void*)p, sizeof(U), tag_); + new (p) U(boost::forward(args)...); } - 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); } +#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_; } diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index ae50fa18..a53f43c3 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,92 @@ struct convertible_to_piecewise UNORDERED_AUTO_TEST(map_emplace_test2) { - boost::unordered_map x; + // Emulating piecewise construction with boost::tuple bypasses the + // allocator's construct method, but still uses test destroy method. + test::detail::disable_construction_tracking _scoped; - 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()); + { + boost::unordered_map, + std::equal_to, + test::allocator1 > > + x; - 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(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(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(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()); - 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.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.clear(); + 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.try_emplace(overloaded_constructor()); - BOOST_TEST( - x.find(overloaded_constructor()) != x.end() && - x.find(overloaded_constructor())->second == overloaded_constructor()); + x.clear(); - 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()); + BOOST_TEST(x.find(overloaded_constructor()) != x.end() && + x.find(overloaded_constructor())->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(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(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) @@ -1158,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()