diff --git a/.travis.yml b/.travis.yml index 652af9b8..14c0168a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,49 +18,47 @@ addons: matrix: include: - compiler: gcc - env: BJAM_TOOLSET=gcc + env: | + label="gcc C++03"; + user_config="using gcc : : g++-4.8 --coverage -fsanitize=address -Werror --std=c++03 ;" + enable_coverage=1 - compiler: gcc - env: BJAM_TOOLSET=gcc-std11 - #- compiler: gcc - # env: BJAM_TOOLSET=gcc-m32 + env: | + label="gcc C++11"; + user_config="using gcc : : g++-4.8 --coverage -fsanitize=address -Werror --std=c++11 ;" + enable_coverage=1 - compiler: gcc - env: BJAM_TOOLSET=gcc-std11m32 - #- compiler: clang - # env: BJAM_TOOLSET=clang + env: | + label="gcc 32 bit C++11"; + user_config="using gcc : : g++-4.8 -m32 -fsanitize=address -Werror --std=c++11 ;" - compiler: clang - env: BJAM_TOOLSET=clang-std11 + env: | + label="clang C++11"; + user_config="using clang : : clang++ -fsanitize=address -Werror --std=c++11 ;" + # sanitized=address not available for 32-bit clang on travis. - compiler: clang - env: BJAM_TOOLSET=clang-m32 - #- compiler: clang - # env: BJAM_TOOLSET=clang-std11m32 - - compiler: gcc - env: BJAM_TOOLSET=gcc-interopable - - compiler: clang - env: BJAM_TOOLSET=clang-interopable + env: | + label="clang 32 bit"; + user_config="using clang : : clang++ -m32 -Werror --std=c++03 ;" + +before_install: + - if [ -n $enable_coverage ]; then pip install --user cpp-coveralls; fi before_script: - cd ${TRAVIS_BUILD_DIR} - touch Jamroot.jam - cd $HOME - - | - echo "using gcc : : g++-4.8 -Werror --std=c++03 -fsanitize=address ;" > ~/user-config.jam - echo "using gcc : std11 : g++-4.8 -Werror --std=c++11 -fsanitize=address ;" >> ~/user-config.jam - echo "using gcc : m32 : g++-4.8 -m32 -Werror -fsanitize=address ;" >> ~/user-config.jam - echo "using gcc : std11m32 : g++-4.8 -m32 -Werror --std=c++11 -fsanitize=address ;" >> ~/user-config.jam - echo "using clang : : clang++ -Werror --std=c++03 -fsanitize=address ;" >> ~/user-config.jam - echo "using clang : std11 : clang++ -Werror --std=c++11 -fsanitize=address ;" >> ~/user-config.jam - # sanitized=address not available for 32-bit clang on travis. - echo "using clang : m32 : clang++ -m32 -Werror --std=c++03 ;" >> ~/user-config.jam - echo "using clang : std11m32 : clang++ -m32 -Werror --std=c++11 ;" >> ~/user-config.jam - echo "using gcc : interopable : g++-4.8 -Werror --std=c++03 -fsanitize=address -DBOOST_UNORDERED_INTEROPERABLE_NODES=1 ;" >> ~/user-config.jam - echo "using clang : interopable : clang++ -Werror --std=c++11 -fsanitize=address -DBOOST_UNORDERED_INTEROPERABLE_NODES=1 ;" >> ~/user-config.jam + - echo $user_config > ~/user-config.jam - cat ~/user-config.jam - - wget -O boost.tar.bz2 https://sourceforge.net/projects/boost/files/boost/1.63.0/boost_1_63_0.tar.bz2 + - wget -O boost.tar.bz2 https://dl.bintray.com/boostorg/release/1.64.0/source/boost_1_64_0.tar.bz2 - tar -xjf boost.tar.bz2 - - mv boost_1_63_0 boost + - mv boost_1_64_0 boost - rm -r boost/boost/unordered +after_success: + if [ -n $enable_coverage ]; then coveralls -r ${TRAVIS_BUILD_DIR} -b ${TRAVIS_BUILD_DIR}/test --gcov-options '\-lp' --include include/boost/unordered/ ; fi + script: - cd ${TRAVIS_BUILD_DIR}/test - - bjam ${BJAM_TOOLSET} include=${HOME}/boost include=${TRAVIS_BUILD_DIR}/include + - bjam -q include=${HOME}/boost include=${TRAVIS_BUILD_DIR}/include - xmllint --noout ${TRAVIS_BUILD_DIR}/doc/ref.xml diff --git a/doc/changes.qbk b/doc/changes.qbk index 3a294bb2..9fa3f753 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -99,7 +99,7 @@ First official release. has been rewritten to use templates instead of macros for the implementation classes. -* The container objcet is now smaller thanks to using `boost::compressed_pair` +* The container object is now smaller thanks to using `boost::compressed_pair` for EBO and a slightly different function buffer - now using a bool instead of a member pointer. @@ -170,7 +170,7 @@ C++11 support has resulted in some breaking changes: longer does so. It does emulate the new `piecewise_construct` pair constructors - only you need to use `boost::piecewise_construct`. To use the old emulation of - the variadic consturctors define + the variadic constructors define `BOOST_UNORDERED_DEPRECATED_PAIR_CONSTRUCT`. [h2 Boost 1.49.0] @@ -207,7 +207,7 @@ C++11 support has resulted in some breaking changes: * Fix construction/destruction issue when using a C++11 compiler with a C++03 allocator ([ticket 7100]). * Remove a `try..catch` to support compiling without exceptions. -* Adjust SFINAE use to try to supprt g++ 3.4 ([ticket 7175]). +* Adjust SFINAE use to try to support g++ 3.4 ([ticket 7175]). * Updated to use the new config macros. [h2 Boost 1.52.0] @@ -307,9 +307,17 @@ C++11 support has resulted in some breaking changes: * Initial support for new C++17 member functions: `insert_or_assign` and `try_emplace` in `unordered_map`, * Initial support for `merge` and `extract`. - Does not include transfering nodes between + Does not include transferring nodes between `unordered_map` and `unordered_multimap` or between `unordered_set` and `unordered_multiset` yet. That will hopefully be in the next version of Boost. +[h2 Boost 1.65.0] + +* Add deprecated attributes to `quick_erase` and `erase_return_void`. + I really will remove them in a future version this time. +* Small standards compliance fixes: + * `noexpect` specs for `swap` free functions. + * Add missing `insert(P&&)` methods. + [endsect] diff --git a/doc/compliance.qbk b/doc/compliance.qbk index 771e473d..711ab3f8 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/doc/rationale.qbk b/doc/rationale.qbk index 788cabc8..60f0849b 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -13,7 +13,7 @@ containers in the draft standard, so the interface was fixed. But there are still some implementation decisions to make. The priorities are conformance to the standard and portability. -The [@http://en.wikipedia.org/wiki/Hash_table wikipedia article on hash tables] +The [@http://en.wikipedia.org/wiki/Hash_table Wikipedia article on hash tables] has a good summary of the implementation issues for hash tables in general. [h2 Data Structure] @@ -100,7 +100,7 @@ knowledge of the number of bits in the hash value, so it isn't portable enough to use as a default. It can applicable in certain cases so the containers have a policy based implementation that can use this alternative technique. -Currently this is only done on 64 bit architecures, where prime number +Currently this is only done on 64 bit architectures, where prime number modulus can be expensive. Although this varies depending on the architecture, so I probably should revisit it. diff --git a/include/boost/unordered/detail/fwd.hpp b/include/boost/unordered/detail/fwd.hpp index 6a842042..2e0c6307 100644 --- a/include/boost/unordered/detail/fwd.hpp +++ b/include/boost/unordered/detail/fwd.hpp @@ -20,7 +20,7 @@ #endif #elif defined(_LIBCPP_VERSION) // https://github.com/llvm-mirror/libcxx/blob/release_30/include/utility#L206 -#if LIBCPP_VERSION >= 3000 +#if _LIBCPP_VERSION >= 3000 #define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 1 #endif #elif defined(BOOST_MSVC) @@ -32,6 +32,8 @@ #endif #endif +// TODO: Use piecewise construction by default? Is it safe to assume that an +// unknown library has it? #if !defined(BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT) #define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 0 #endif diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index c293ae2f..5750a4c6 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1,4 +1,3 @@ - // Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. // Copyright (C) 2005-2016 Daniel James // @@ -19,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -47,10 +48,6 @@ #include #include -#if !defined(BOOST_NO_CXX11_HDR_TUPLE) -#include -#endif - #if !defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS) #include #endif @@ -61,24 +58,25 @@ // Unless documented elsewhere these configuration macros should be considered // an implementation detail, I'll try not to break them, but you never know. -// BOOST_UNORDERED_EMPLACE_LIMIT = The maximum number of parameters in emplace -// (not including things like hints). Don't set it to a lower value, as that -// might break something. +// Use Sun C++ workarounds +// I'm not sure which versions of the compiler require these workarounds, so +// I'm just using them of everything older than the current test compilers +// (as of May 2017). -#if !defined BOOST_UNORDERED_EMPLACE_LIMIT -#define BOOST_UNORDERED_EMPLACE_LIMIT 11 +#if !defined(BOOST_UNORDERED_SUN_WORKAROUNDS1) +#if BOOST_COMP_SUNPRO && BOOST_COMP_SUNPRO < BOOST_VERSION_NUMBER(5, 20, 0) +#define BOOST_UNORDERED_SUN_WORKAROUNDS1 1 +#else +#define BOOST_UNORDERED_SUN_WORKAROUNDS1 0 +#endif #endif -// BOOST_UNORDERED_INTEROPERABLE_NODES - Use the same node type for -// containers with unique and equivalent keys. -// -// 0 = Use different nodes -// 1 = Use ungrouped nodes everywhere -// -// Might add an extra value to use grouped nodes everywhere later. +// BOOST_UNORDERED_EMPLACE_LIMIT = The maximum number of parameters in +// emplace (not including things like hints). Don't set it to a lower value, as +// that might break something. -#if !defined(BOOST_UNORDERED_INTEROPERABLE_NODES) -#define BOOST_UNORDERED_INTEROPERABLE_NODES 0 +#if !defined BOOST_UNORDERED_EMPLACE_LIMIT +#define BOOST_UNORDERED_EMPLACE_LIMIT 10 #endif // BOOST_UNORDERED_USE_ALLOCATOR_TRAITS - Pick which version of @@ -104,13 +102,106 @@ #define BOOST_UNORDERED_USE_ALLOCATOR_TRAITS 0 #endif +// BOOST_UNORDERED_TUPLE_ARGS +// +// Maximum number of std::tuple members to support, or 0 if std::tuple +// isn't avaiable. More are supported when full C++11 is used. + +// Already defined, so do nothing +#if defined(BOOST_UNORDERED_TUPLE_ARGS) + +// Assume if we have C++11 tuple it's properly variadic, +// and just use a max number of 10 arguments. +#elif !defined(BOOST_NO_CXX11_HDR_TUPLE) +#define BOOST_UNORDERED_TUPLE_ARGS 10 + +// Visual C++ has a decent enough tuple for piecewise construction, +// so use that if available, using _VARIADIC_MAX for the maximum +// number of parameters. Note that this comes after the check +// for a full C++11 tuple. +#elif defined(BOOST_MSVC) +#if !BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT +#define BOOST_UNORDERED_TUPLE_ARGS 0 +#elif defined(_VARIADIC_MAX) +#define BOOST_UNORDERED_TUPLE_ARGS _VARIADIC_MAX +#else +#define BOOST_UNORDERED_TUPLE_ARGS 5 +#endif + +// Assume that we don't have std::tuple +#else +#define BOOST_UNORDERED_TUPLE_ARGS 0 +#endif + +#if BOOST_UNORDERED_TUPLE_ARGS +#include +#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) && BOOST_UNORDERED_TUPLE_ARGS +#if BOOST_COMP_SUNPRO && BOOST_LIB_STD_GNU +// Sun C++ std::pair piecewise construction doesn't seem to be exception safe. +// (At least for Sun C++ 12.5 using libstdc++). +#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0 +#elif BOOST_COMP_GNUC && BOOST_COMP_GNUC < BOOST_VERSION_NUMBER(4, 7, 0) +// Piecewise construction in GCC 4.6 doesn't work for uncopyable types. +#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0 +#elif 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 + +// BOOST_UNORDERED_SUPPRESS_DEPRECATED +// +// Define to stop deprecation attributes + +#if defined(BOOST_UNORDERED_SUPPRESS_DEPRECATED) +#define BOOST_UNORDERED_DEPRECATED(msg) +#endif + +// BOOST_UNORDERED_DEPRECATED +// +// Wrapper around various depreaction attributes. + +#if defined(__has_cpp_attribute) && \ + (!defined(__cplusplus) || __cplusplus >= 201402) +#if __has_cpp_attribute(deprecated) && !defined(BOOST_UNORDERED_DEPRECATED) +#define BOOST_UNORDERED_DEPRECATED(msg) [[deprecated(msg)]] +#endif +#endif + +#if !defined(BOOST_UNORDERED_DEPRECATED) +#if defined(__GNUC__) && __GNUC__ >= 4 +#define BOOST_UNORDERED_DEPRECATED(msg) __attribute__((deprecated)) +#elif defined(_MSC_VER) && _MSC_VER >= 1400 +#define BOOST_UNORDERED_DEPRECATED(msg) __declspec(deprecated(msg)) +#elif defined(_MSC_VER) && _MSC_VER >= 1310 +#define BOOST_UNORDERED_DEPRECATED(msg) __declspec(deprecated) +#else +#define BOOST_UNORDERED_DEPRECATED(msg) +#endif +#endif + namespace boost { namespace unordered { namespace iterator_detail { template struct iterator; template struct c_iterator; -template struct l_iterator; -template struct cl_iterator; +template struct l_iterator; +template struct cl_iterator; } } } @@ -122,28 +213,27 @@ namespace detail { template struct table; template struct bucket; struct ptr_bucket; -template struct table_impl; -template struct grouped_table_impl; -template struct unique_node; +template struct node; template struct ptr_node; -template struct table_impl; - -template struct grouped_node; -template struct grouped_ptr_node; -template struct grouped_table_impl; -template struct node_algo; -template struct grouped_node_algo; static const float minimum_max_load_factor = 1e-3f; static const std::size_t default_bucket_count = 11; + struct move_tag { }; + struct empty_emplace { }; +struct no_key +{ + no_key() {} + template no_key(T const&) {} +}; + namespace func { template inline void ignore_unused_variable_warning(T const&) {} } @@ -190,7 +280,7 @@ template struct prime_list_template { static std::size_t const value[]; -#if !defined(SUNPRO_CC) +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 static std::ptrdiff_t const length; #else static std::ptrdiff_t const length = @@ -202,7 +292,7 @@ template std::size_t const prime_list_template::value[] = { BOOST_PP_SEQ_ENUM(BOOST_UNORDERED_PRIMES)}; -#if !defined(SUNPRO_CC) +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 template std::ptrdiff_t const prime_list_template::length = BOOST_PP_SEQ_SIZE( BOOST_UNORDERED_PRIMES); @@ -439,16 +529,17 @@ 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) +// Get a pointer from a smart pointer, a bit simpler than pointer_traits +// as we already know the pointer type that we want. +template struct pointer { - return const_cast(x); -} -} + template static T* get(Ptr const& x) + { + return static_cast(x.operator->()); + } + + template static T* get(T2* x) { return static_cast(x); } +}; //////////////////////////////////////////////////////////////////////////// // emplace_args @@ -458,20 +549,12 @@ template T* const_cast_pointer(T const* x) #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) -#define BOOST_UNORDERED_EMPLACE_ARGS1(a0) a0 -#define BOOST_UNORDERED_EMPLACE_ARGS2(a0, a1) a0, a1 -#define BOOST_UNORDERED_EMPLACE_ARGS3(a0, a1, a2) a0, a1, a2 - #define BOOST_UNORDERED_EMPLACE_TEMPLATE typename... Args #define BOOST_UNORDERED_EMPLACE_ARGS BOOST_FWD_REF(Args)... args #define BOOST_UNORDERED_EMPLACE_FORWARD boost::forward(args)... #else -#define BOOST_UNORDERED_EMPLACE_ARGS1 create_emplace_args -#define BOOST_UNORDERED_EMPLACE_ARGS2 create_emplace_args -#define BOOST_UNORDERED_EMPLACE_ARGS3 create_emplace_args - #define BOOST_UNORDERED_EMPLACE_TEMPLATE typename Args #define BOOST_UNORDERED_EMPLACE_ARGS Args const& args #define BOOST_UNORDERED_EMPLACE_FORWARD args @@ -491,7 +574,8 @@ template T* const_cast_pointer(T const* x) #endif -template struct emplace_args1 +template +struct emplace_args1 { BOOST_UNORDERED_EARGS_MEMBER(1, 0, _) @@ -568,8 +652,14 @@ inline emplace_args3 create_emplace_args( return e; \ } +BOOST_UNORDERED_EARGS(1, 4, _) +BOOST_UNORDERED_EARGS(1, 5, _) +BOOST_UNORDERED_EARGS(1, 6, _) +BOOST_UNORDERED_EARGS(1, 7, _) +BOOST_UNORDERED_EARGS(1, 8, _) +BOOST_UNORDERED_EARGS(1, 9, _) BOOST_PP_REPEAT_FROM_TO( - 4, BOOST_UNORDERED_EMPLACE_LIMIT, BOOST_UNORDERED_EARGS, _) + 10, BOOST_PP_INC(BOOST_UNORDERED_EMPLACE_LIMIT), BOOST_UNORDERED_EARGS, _) #undef BOOST_UNORDERED_DEFINE_EMPLACE_ARGS #undef BOOST_UNORDERED_EARGS_MEMBER @@ -738,13 +828,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 { @@ -1013,7 +1096,7 @@ template struct allocator_traits } public: -#if BOOST_UNORDERED_DETAIL_FULL_CONSTRUCT +#if BOOST_UNORDERED_CXX11_CONSTRUCTION template static typename boost::enable_if_c< @@ -1168,8 +1251,6 @@ template struct allocator_traits #include -#define BOOST_UNORDERED_DETAIL_FULL_CONSTRUCT 1 - namespace boost { namespace unordered { namespace detail { @@ -1195,8 +1276,6 @@ template struct rebind_wrap #include -#define BOOST_UNORDERED_DETAIL_FULL_CONSTRUCT 0 - namespace boost { namespace unordered { namespace detail { @@ -1231,157 +1310,160 @@ 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_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_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_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<>) \ - { \ - boost::unordered::detail::func::call_construct(alloc, ptr); \ - } \ - \ - BOOST_PP_REPEAT_FROM_TO( \ - 1, n, BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE_IMPL, namespace_) - -#define BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE_IMPL(z, n, namespace_) \ - template \ - void construct_from_tuple(Alloc& alloc, T* ptr, \ - namespace_ tuple const& x) \ - { \ - boost::unordered::detail::func::call_construct(alloc, ptr, \ - BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_GET_TUPLE_ARG, namespace_)); \ - } - -#define BOOST_UNORDERED_GET_TUPLE_ARG(z, n, namespace_) namespace_ get(x) - -#elif !defined(__SUNPRO_CC) - -#define BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(n, namespace_) \ - template \ - void construct_from_tuple(Alloc&, T* ptr, namespace_ tuple<>) \ - { \ - new ((void*)ptr) T(); \ - } \ - \ - BOOST_PP_REPEAT_FROM_TO( \ - 1, n, BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE_IMPL, namespace_) - -#define BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE_IMPL(z, n, namespace_) \ +#define BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(z, n, namespace_) \ template \ void construct_from_tuple(Alloc&, T* ptr, \ - namespace_ tuple const& x) \ + namespace_::tuple const& x) \ { \ new ((void*)ptr) T( \ BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_GET_TUPLE_ARG, namespace_)); \ } -#define BOOST_UNORDERED_GET_TUPLE_ARG(z, n, namespace_) namespace_ get(x) +#define BOOST_UNORDERED_GET_TUPLE_ARG(z, n, namespace_) namespace_::get(x) -#else +// construct_from_tuple for boost::tuple +// The workaround for old Sun compilers comes later in the file. + +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 + +template +void construct_from_tuple(Alloc&, T* ptr, boost::tuple<>) +{ + new ((void*)ptr) T(); +} + +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 1, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 2, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 3, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 4, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 5, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 6, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 7, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 8, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 9, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 10, boost) + +#endif + +// construct_from_tuple for std::tuple + +#if !BOOST_UNORDERED_CXX11_CONSTRUCTION && BOOST_UNORDERED_TUPLE_ARGS + +template +void construct_from_tuple(Alloc&, T* ptr, std::tuple<>) +{ + new ((void*)ptr) T(); +} + +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 1, std) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 2, std) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 3, std) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 4, std) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 5, std) + +#if BOOST_UNORDERED_TUPLE_ARGS >= 6 +BOOST_PP_REPEAT_FROM_TO(6, BOOST_PP_INC(BOOST_UNORDERED_TUPLE_ARGS), + BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE, std) +#endif + +#endif + +#undef BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE +#undef BOOST_UNORDERED_GET_TUPLE_ARG + +// construct_from_tuple for boost::tuple on old versions of sunpro. +// +// Old versions of Sun C++ had problems with template overloads of +// boost::tuple, so to fix it I added a distinct type for each length to +// the overloads. That means there's no possible ambiguity between the +// different overloads, so that the compiler doesn't get confused + +#if BOOST_UNORDERED_SUN_WORKAROUNDS1 template struct length { }; -#define BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(n, namespace_) \ - template \ - void construct_from_tuple_impl(boost::unordered::detail::func::length<0>, \ - Alloc&, T* ptr, namespace_ tuple<>) \ - { \ - new ((void*)ptr) T(); \ - } \ - \ - BOOST_PP_REPEAT_FROM_TO( \ - 1, n, BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE_IMPL, namespace_) - -#define BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE_IMPL(z, n, namespace_) \ +#define BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(z, n, namespace_) \ template \ void construct_from_tuple_impl(boost::unordered::detail::func::length, \ Alloc&, T* ptr, \ - namespace_ tuple const& x) \ + namespace_::tuple const& x) \ { \ new ((void*)ptr) T( \ BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_GET_TUPLE_ARG, namespace_)); \ } -#define BOOST_UNORDERED_GET_TUPLE_ARG(z, n, namespace_) namespace_ get(x) +#define BOOST_UNORDERED_GET_TUPLE_ARG(z, n, namespace_) namespace_::get(x) -#endif +template +void construct_from_tuple_impl( + boost::unordered::detail::func::length<0>, Alloc&, T* ptr, boost::tuple<>) +{ + new ((void*)ptr) T(); +} -BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(10, boost::) - -#if !defined(__SUNPRO_CC) && !defined(BOOST_NO_CXX11_HDR_TUPLE) -BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(10, std::) -#endif - -#undef BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE -#undef BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE_IMPL -#undef BOOST_UNORDERED_GET_TUPLE_ARG - -#if defined(__SUNPRO_CC) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 1, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 2, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 3, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 4, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 5, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 6, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 7, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 8, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 9, boost) +BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE(1, 10, boost) template void construct_from_tuple(Alloc& alloc, T* ptr, Tuple const& x) @@ -1391,6 +1473,9 @@ void construct_from_tuple(Alloc& alloc, T* ptr, Tuple const& x) alloc, ptr, x); } +#undef BOOST_UNORDERED_CONSTRUCT_FROM_TUPLE +#undef BOOST_UNORDERED_GET_TUPLE_ARG + #endif //////////////////////////////////////////////////////////////////////////// @@ -1409,25 +1494,77 @@ 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&, T* address, BOOST_FWD_REF(Args)... args) +{ + new ((void*)address) T(boost::forward(args)...); +} + +// Special case for piecewise_construct template @@ -1436,22 +1573,17 @@ 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_RETHROW; + boost::unordered::detail::func::destroy( + boost::addressof(address->first)); + BOOST_RETHROW } BOOST_CATCH_END } @@ -1500,12 +1632,18 @@ inline void construct_from_args( num_params, BOOST_UNORDERED_CALL_FORWARD, args.a)); \ } -BOOST_PP_REPEAT_FROM_TO( - 4, BOOST_UNORDERED_EMPLACE_LIMIT, BOOST_UNORDERED_CONSTRUCT_IMPL, _) +BOOST_UNORDERED_CONSTRUCT_IMPL(1, 4, _) +BOOST_UNORDERED_CONSTRUCT_IMPL(1, 5, _) +BOOST_UNORDERED_CONSTRUCT_IMPL(1, 6, _) +BOOST_UNORDERED_CONSTRUCT_IMPL(1, 7, _) +BOOST_UNORDERED_CONSTRUCT_IMPL(1, 8, _) +BOOST_UNORDERED_CONSTRUCT_IMPL(1, 9, _) +BOOST_PP_REPEAT_FROM_TO(10, BOOST_PP_INC(BOOST_UNORDERED_EMPLACE_LIMIT), + BOOST_UNORDERED_CONSTRUCT_IMPL, _) #undef BOOST_UNORDERED_CONSTRUCT_IMPL -// Construct with piece_construct +// Construct with piecewise_construct template @@ -1514,22 +1652,17 @@ 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_RETHROW; + boost::unordered::detail::func::destroy( + boost::addressof(address->first)); + BOOST_RETHROW } BOOST_CATCH_END } @@ -1559,12 +1692,8 @@ template struct node_constructor node_allocator& alloc_; node_pointer node_; - bool node_constructed_; - node_constructor(node_allocator& n) - : alloc_(n), node_(), node_constructed_(false) - { - } + node_constructor(node_allocator& n) : alloc_(n), node_() {} ~node_constructor(); @@ -1573,7 +1702,7 @@ template struct node_constructor // no throw node_pointer release() { - BOOST_ASSERT(node_ && node_constructed_); + BOOST_ASSERT(node_); node_pointer p = node_; node_ = node_pointer(); return p; @@ -1583,9 +1712,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: @@ -1596,10 +1724,7 @@ template struct node_constructor template node_constructor::~node_constructor() { if (node_) { - if (node_constructed_) { - boost::unordered::detail::func::destroy(boost::addressof(*node_)); - } - + boost::unordered::detail::func::destroy(pointer::get(node_)); node_allocator_traits::deallocate(alloc_, node_, 1); } } @@ -1607,13 +1732,8 @@ template node_constructor::~node_constructor() template void node_constructor::create_node() { BOOST_ASSERT(!node_); - node_constructed_ = false; - node_ = node_allocator_traits::allocate(alloc_, 1); - - new ((void*)boost::addressof(*node_)) node(); - node_->init(node_); - node_constructed_ = true; + new (pointer::get(node_)) node(); } template struct node_tmp @@ -1621,6 +1741,7 @@ template struct node_tmp typedef boost::unordered::detail::allocator_traits node_allocator_traits; typedef typename node_allocator_traits::pointer node_pointer; + typedef typename node_allocator_traits::value_type node; NodeAlloc& alloc_; node_pointer node_; @@ -1641,9 +1762,9 @@ 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::detail::func::destroy(pointer::get(node_)); node_allocator_traits::deallocate(alloc_, node_, 1); } } @@ -1676,35 +1797,84 @@ 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(); +#if !(BOOST_COMP_CLANG && BOOST_COMP_CLANG < BOOST_VERSION_NUMBER(3, 8, 0) && \ + defined(BOOST_LIBSTDCXX11)) + 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)...)); +#else + // It doesn't seem to be possible to construct a tuple with 3 variadic + // rvalue reference members when using older versions of clang with + // libstdc++, so just use std::make_tuple instead of 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::make_tuple(boost::forward(args)...)); +#endif + 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_RETHROW; + boost::unordered::detail::func::destroy( + boost::addressof(a.node_->value_ptr()->first)); + BOOST_RETHROW } BOOST_CATCH_END return a.release(); @@ -1716,23 +1886,19 @@ 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_RETHROW; + boost::unordered::detail::func::destroy( + boost::addressof(a.node_->value_ptr()->first)); + BOOST_RETHROW } BOOST_CATCH_END return a.release(); @@ -1745,27 +1911,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_RETHROW; + boost::unordered::detail::func::destroy( + boost::addressof(a.node_->value_ptr()->first)); + BOOST_RETHROW } BOOST_CATCH_END return a.release(); } + +#endif } } } @@ -1788,13 +1952,13 @@ namespace iterator_detail { // // all no throw -template +template struct l_iterator : public std::iterator { #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) - template + template friend struct boost::unordered::iterator_detail::cl_iterator; private: @@ -1823,7 +1987,7 @@ struct l_iterator : public std::iterator(ptr_->next_); - if (ptr_ && Policy::to_bucket(bucket_count_, ptr_->hash_) != bucket_) + if (ptr_ && ptr_->get_bucket() != bucket_) ptr_ = node_pointer(); return *this; } @@ -1846,13 +2010,13 @@ struct l_iterator : public std::iterator +template struct cl_iterator : public std::iterator { - friend struct boost::unordered::iterator_detail::l_iterator; + friend struct boost::unordered::iterator_detail::l_iterator; private: typedef typename Node::node_pointer node_pointer; @@ -1872,8 +2036,7 @@ struct cl_iterator { } - cl_iterator( - boost::unordered::iterator_detail::l_iterator const& x) + cl_iterator(boost::unordered::iterator_detail::l_iterator const& x) BOOST_NOEXCEPT : ptr_(x.ptr_), bucket_(x.bucket_), bucket_count_(x.bucket_count_) @@ -1887,7 +2050,7 @@ struct cl_iterator cl_iterator& operator++() { ptr_ = static_cast(ptr_->next_); - if (ptr_ && Policy::to_bucket(bucket_count_, ptr_->hash_) != bucket_) + if (ptr_ && ptr_->get_bucket() != bucket_) ptr_ = node_pointer(); return *this; } @@ -1921,9 +2084,6 @@ struct iterator : public std::iterator friend struct boost::unordered::iterator_detail::c_iterator; template friend struct boost::unordered::detail::table; - template friend struct boost::unordered::detail::table_impl; - template - friend struct boost::unordered::detail::grouped_table_impl; private: #endif @@ -1978,9 +2138,6 @@ struct c_iterator #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) template friend struct boost::unordered::detail::table; - template friend struct boost::unordered::detail::table_impl; - template - friend struct boost::unordered::detail::grouped_table_impl; private: #endif @@ -2076,7 +2233,6 @@ template struct node_holder { node_pointer n = nodes_; nodes_ = static_cast(nodes_->next_); - n->init(n); n->next_ = link_pointer(); return n; } @@ -2088,7 +2244,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(); } @@ -2100,8 +2256,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(); } @@ -2114,9 +2271,9 @@ 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::detail::func::destroy(pointer::get(p)); node_allocator_traits::deallocate(constructor_.alloc_, p, 1); } } @@ -2131,6 +2288,7 @@ template struct bucket link_pointer next_; bucket() : next_() {} + bucket(link_pointer n) : next_(n) {} link_pointer first_from_start() { return next_; } @@ -2146,6 +2304,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; } @@ -2332,13 +2491,13 @@ template class functions function_pair const& current() const { return *static_cast( - static_cast(&funcs_[current_])); + static_cast(funcs_[current_].address())); } function_pair& current() { return *static_cast( - static_cast(&funcs_[current_])); + static_cast(funcs_[current_].address())); } void construct(bool which, H const& hf, P const& eq) @@ -2549,7 +2708,6 @@ struct table : boost::unordered::detail::functions @@ -2572,6 +2730,8 @@ struct table : boost::unordered::detail::functions node_tmp; + typedef std::pair emplace_return; + //////////////////////////////////////////////////////////////////////// // Members @@ -2586,6 +2746,45 @@ struct table : boost::unordered::detail::functions(n->next_); + } + + static node_pointer next_for_find(link_pointer n) + { + node_pointer n2 = static_cast(n); + do { + n2 = next_node(n2); + } while (n2 && !n2->is_first_in_group()); + return n2; + } + + node_pointer next_group(node_pointer n) const + { + node_pointer n1 = n; + do { + n1 = next_node(n1); + } while (n1 && !n1->is_first_in_group()); + return n1; + } + + std::size_t group_count(node_pointer n) const + { + std::size_t x = 0; + node_pointer it = n; + do { + ++x; + it = next_node(it); + } while (it && !it->is_first_in_group()); + + return x; + } + + std::size_t node_bucket(node_pointer n) const { return n->get_bucket(); } + bucket_allocator const& bucket_alloc() const { return allocators_.first(); } node_allocator const& node_alloc() const { return allocators_.second(); } @@ -2619,8 +2818,7 @@ struct table : boost::unordered::detail::functions(size_) / static_cast(bucket_count_); - } - std::size_t bucket_size(std::size_t index) const { node_pointer n = begin(index); @@ -2649,9 +2841,9 @@ struct table : boost::unordered::detail::functionshash_) == index) { + while (n && node_bucket(n) == index) { ++count; - n = node_algo::next_node(n); + n = next_node(n); } return count; @@ -2660,17 +2852,6 @@ struct table : boost::unordered::detail::functions(mlf_) * - static_cast(max_bucket_count()))) - - 1; - } - void recalculate_max_load() { using namespace std; @@ -2742,79 +2923,68 @@ struct table : boost::unordered::detail::functions(this)->copy_buckets(x); + bucket_pointer end = get_bucket(bucket_count_); + for (bucket_pointer it = buckets_; it != end; ++it) { + it->next_ = node_pointer(); } } - void move_init(table& x) - { - if (node_alloc() == x.node_alloc()) { - move_buckets_from(x); - } else if (x.size_) { - // TODO: Could pick new bucket size? - static_cast(this)->move_buckets(x); - } - } - - //////////////////////////////////////////////////////////////////////// - // Create buckets - + // Create container buckets. If the container already contains any buckets + // the linked list will be transferred to the new buckets, but none + // of the bucket pointers will be set. See above note. + // + // Strong exception safety. void create_buckets(std::size_t new_count) { - std::size_t length = new_count + 1; - bucket_pointer new_buckets = - bucket_allocator_traits::allocate(bucket_alloc(), length); - bucket_pointer constructed = new_buckets; + link_pointer dummy_node; - BOOST_TRY - { - bucket_pointer end = - new_buckets + static_cast(length); - for (; constructed != end; ++constructed) { - new ((void*)boost::addressof(*constructed)) bucket(); - } - - if (buckets_) { - // Copy the nodes to the new buckets, including the dummy - // node if there is one. - (new_buckets + static_cast(new_count))->next_ = - (buckets_ + static_cast(bucket_count_)) - ->next_; - destroy_buckets(); - } else if (bucket::extra_node) { - node_constructor a(node_alloc()); - a.create_node(); - - (new_buckets + static_cast(new_count))->next_ = - a.release(); - } + // Construct the new buckets and dummy node, and destroy the old buckets + if (buckets_) { + dummy_node = + (buckets_ + static_cast(bucket_count_))->next_; + bucket_pointer new_buckets = bucket_allocator_traits::allocate( + bucket_alloc(), new_count + 1); + destroy_buckets(); + buckets_ = new_buckets; + } else if (bucket::extra_node) { + node_constructor a(node_alloc()); + a.create_node(); + buckets_ = bucket_allocator_traits::allocate( + bucket_alloc(), new_count + 1); + dummy_node = a.release(); + } else { + dummy_node = link_pointer(); + buckets_ = bucket_allocator_traits::allocate( + bucket_alloc(), new_count + 1); } - BOOST_CATCH(...) - { - for (bucket_pointer p = new_buckets; p != constructed; ++p) { - boost::unordered::detail::func::destroy(boost::addressof(*p)); - } - - bucket_allocator_traits::deallocate( - bucket_alloc(), new_buckets, length); - - BOOST_RETHROW; - } - BOOST_CATCH_END + // nothrow from here... bucket_count_ = new_count; - buckets_ = new_buckets; recalculate_max_load(); + + bucket_pointer end = buckets_ + static_cast(new_count); + for (bucket_pointer i = buckets_; i != end; ++i) { + new (pointer::get(i)) bucket(); + } + new (pointer::get(end)) bucket(dummy_node); } //////////////////////////////////////////////////////////////////////// @@ -2865,6 +3035,7 @@ 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::detail::func::destroy(pointer::get(n)); node_allocator_traits::deallocate(node_alloc(), n, 1); - --size_; - } - - std::size_t delete_nodes(link_pointer prev, link_pointer end) - { - BOOST_ASSERT(prev->next_ != end); - - std::size_t count = 0; - - do { - delete_node(prev); - ++count; - } while (prev->next_ != end); - - return count; } void delete_buckets() { if (buckets_) { - if (size_) - delete_nodes(get_previous_start(), link_pointer()); + node_pointer n = + static_cast(get_bucket(bucket_count_)->next_); if (bucket::extra_node) { - node_pointer n = - static_cast(get_bucket(bucket_count_)->next_); - boost::unordered::detail::func::destroy(boost::addressof(*n)); + node_pointer next = next_node(n); + boost::unordered::detail::func::destroy(pointer::get(n)); node_allocator_traits::deallocate(node_alloc(), n, 1); + n = next; + } + + while (n) { + node_pointer next = next_node(n); + destroy_node(n); + n = next; } destroy_buckets(); buckets_ = bucket_pointer(); max_load_ = 0; - } - - BOOST_ASSERT(!size_); - } - - void clear() - { - if (!size_) - return; - - delete_nodes(get_previous_start(), link_pointer()); - clear_buckets(); - - BOOST_ASSERT(!size_); - } - - void clear_buckets() - { - bucket_pointer end = get_bucket(bucket_count_); - for (bucket_pointer it = buckets_; it != end; ++it) { - it->next_ = node_pointer(); + size_ = 0; } } @@ -2945,7 +3084,7 @@ struct table : boost::unordered::detail::functions::get(it)); } bucket_allocator_traits::deallocate( @@ -2953,74 +3092,81 @@ struct table : boost::unordered::detail::functionsnext_; std::size_t bucket_index2 = bucket_index; - if (end) { - bucket_index2 = - hash_to_bucket(static_cast(end)->hash_); + if (next) { + bucket_index2 = node_bucket(next); - // If begin and end are in the same bucket, then - // there's nothing to do. - if (bucket_index == bucket_index2) + // If next is in the same bucket, then there's nothing to do. + if (bucket_index == bucket_index2) { return bucket_index2; + } - // Update the bucket containing end. + // Update the bucket containing next. get_bucket(bucket_index2)->next_ = prev; } // Check if this bucket is now empty. bucket_pointer this_bucket = get_bucket(bucket_index); - if (this_bucket->next_ == prev) + if (this_bucket->next_ == prev) { this_bucket->next_ = link_pointer(); + } return bucket_index2; } + //////////////////////////////////////////////////////////////////////// + // Clear + + void clear_impl(); + //////////////////////////////////////////////////////////////////////// // Assignment - void assign(table const& x) + template + void assign(table const& x, UniqueType is_unique) { - if (this != boost::addressof(x)) { - assign(x, boost::unordered::detail::integral_constant:: - propagate_on_container_copy_assignment::value>()); + if (this != &x) { + assign(x, is_unique, + boost::unordered::detail::integral_constant:: + propagate_on_container_copy_assignment::value>()); } } - void assign(table const& x, false_type) + template + void assign(table const& x, UniqueType is_unique, false_type) { // Strong exception safety. set_hash_functions new_func_this(*this, x); mlf_ = x.mlf_; recalculate_max_load(); - if (!size_ && !x.size_) { - new_func_this.commit(); - return; - } - if (x.size_ >= max_load_) { create_buckets(min_buckets_for_size(x.size_)); - } else { + } else if (size_) { clear_buckets(); } new_func_this.commit(); - static_cast(this)->assign_buckets(x); + + assign_buckets(x, is_unique); } - void assign(table const& x, true_type) + template + void assign(table const& x, UniqueType is_unique, true_type) { if (node_alloc() == x.node_alloc()) { allocators_.assign(x.allocators_); - assign(x, false_type()); + assign(x, is_unique, false_type()); } else { set_hash_functions new_func_this(*this, x); @@ -3033,45 +3179,46 @@ struct table : boost::unordered::detail::functions(this)->copy_buckets(x); + copy_buckets(x, is_unique); } } } - void move_assign(table& x) + template + void move_assign(table& x, UniqueType is_unique) { - if (this != boost::addressof(x)) { + if (this != &x) { move_assign( - x, boost::unordered::detail::integral_constant:: - propagate_on_container_move_assignment::value>()); + x, is_unique, + boost::unordered::detail::integral_constant:: + propagate_on_container_move_assignment::value>()); } } - void move_assign(table& x, true_type) + template + void move_assign(table& x, UniqueType, true_type) { delete_buckets(); set_hash_functions new_func_this(*this, x); allocators_.move_assign(x.allocators_); // No throw from here. mlf_ = x.mlf_; - max_load_ = x.max_load_; move_buckets_from(x); new_func_this.commit(); } - void move_assign(table& x, false_type) + template + void move_assign(table& x, UniqueType is_unique, false_type) { if (node_alloc() == x.node_alloc()) { delete_buckets(); set_hash_functions new_func_this(*this, x); // No throw from here. mlf_ = x.mlf_; - max_load_ = x.max_load_; move_buckets_from(x); new_func_this.commit(); } else { @@ -3079,27 +3226,23 @@ struct table : boost::unordered::detail::functions= max_load_) { create_buckets(min_buckets_for_size(x.size_)); - } else { + } else if (size_) { clear_buckets(); } new_func_this.commit(); - static_cast(this)->move_assign_buckets(x); + + move_assign_buckets(x, is_unique); } } // Accessors - const_key_type& get_key(value_type const& x) const + const_key_type& get_key(node_pointer n) const { - return extractor::extract(x); + return extractor::extract(n->value()); } std::size_t hash(const_key_type& k) const @@ -3109,13 +3252,6 @@ struct table : boost::unordered::detail::functions - node_pointer generic_find_node( - Key const& k, Hash const& hf, Pred const& eq) const - { - return this->find_node_impl(policy::apply_hash(hf, k), k, eq); - } - node_pointer find_node(std::size_t key_hash, const_key_type& k) const { return this->find_node_impl(key_hash, k, this->key_eq()); @@ -3137,22 +3273,18 @@ struct table : boost::unordered::detail::functionshash_; - if (key_hash == node_hash) { - if (eq(k, this->get_key(n->value()))) - return n; - } else { - if (this->hash_to_bucket(node_hash) != bucket_index) - return node_pointer(); + if (eq(k, this->get_key(n))) { + return n; + } else if (this->node_bucket(n) != bucket_index) { + return node_pointer(); } - n = node_algo::next_for_find(n); + n = next_for_find(n); } } // Find the node before the key, so that it can be erased. - link_pointer find_previous_node( - const_key_type& k, std::size_t key_hash, std::size_t bucket_index) + link_pointer find_previous_node(const_key_type& k, std::size_t bucket_index) { link_pointer prev = this->get_previous_start(bucket_index); if (!prev) { @@ -3160,19 +3292,17 @@ struct table : boost::unordered::detail::functionsnext_) { + node_pointer n = next_node(prev); + if (!n) { return link_pointer(); + } else if (n->is_first_in_group()) { + if (node_bucket(n) != bucket_index) { + return link_pointer(); + } else if (this->key_eq()(k, this->get_key(n))) { + return prev; + } } - std::size_t node_hash = node_algo::next_node(prev)->hash_; - if (this->hash_to_bucket(node_hash) != bucket_index) { - return link_pointer(); - } - if (node_hash == key_hash && - this->key_eq()( - k, this->get_key(node_algo::next_node(prev)->value()))) { - return prev; - } - prev = node_algo::next_for_erase(prev); + prev = n; } } @@ -3185,13 +3315,18 @@ struct table : boost::unordered::detail::functionshash(k); std::size_t bucket_index = this->hash_to_bucket(key_hash); - link_pointer prev = this->find_previous_node(k, key_hash, bucket_index); + link_pointer prev = this->find_previous_node(k, bucket_index); if (!prev) { return node_pointer(); } - node_pointer n = node_algo::extract_first_node(prev); + node_pointer n = next_node(prev); + node_pointer n2 = next_node(n); + if (n2) { + n2->set_first_in_group(); + } + prev->next_ = n2; --this->size_; - this->fix_bucket(bucket_index, prev); + this->fix_bucket(bucket_index, prev, n2); n->next_ = link_pointer(); return n; @@ -3203,8 +3338,910 @@ struct table : boost::unordered::detail::functionssize_ != other.size_) + return false; + + for (node_pointer n1 = this->begin(); n1; n1 = next_node(n1)) { + node_pointer n2 = other.find_node(other.get_key(n1)); + + if (!n2 || n1->value() != n2->value()) + return false; + } + + return true; + } + + // Emplace/Insert + + inline node_pointer add_node_unique(node_pointer n, std::size_t key_hash) + { + std::size_t bucket_index = this->hash_to_bucket(key_hash); + bucket_pointer b = this->get_bucket(bucket_index); + + // TODO: Do this need to set_first_in_group ? + n->bucket_info_ = bucket_index; + // n->set_first_in_group(); + + if (!b->next_) { + link_pointer start_node = this->get_previous_start(); + + if (start_node->next_) { + this->get_bucket(node_bucket(next_node(start_node)))->next_ = n; + } + + b->next_ = start_node; + n->next_ = start_node->next_; + start_node->next_ = n; + } else { + n->next_ = b->next_->next_; + b->next_->next_ = n; + } + + ++this->size_; + return n; + } + + inline node_pointer resize_and_add_node_unique( + node_pointer n, std::size_t key_hash) + { + node_tmp b(n, this->node_alloc()); + this->reserve_for_insert(this->size_ + 1); + return this->add_node_unique(b.release(), key_hash); + } + + template + iterator emplace_hint_unique( + c_iterator hint, const_key_type& k, BOOST_UNORDERED_EMPLACE_ARGS) + { + if (hint.node_ && this->key_eq()(k, this->get_key(hint.node_))) { + return iterator(hint.node_); + } else { + return emplace_unique(k, BOOST_UNORDERED_EMPLACE_FORWARD).first; + } + } + + template + emplace_return emplace_unique( + const_key_type& k, BOOST_UNORDERED_EMPLACE_ARGS) + { + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + if (pos) { + return emplace_return(iterator(pos), false); + } else { + return emplace_return( + iterator(this->resize_and_add_node_unique( + boost::unordered::detail::func::construct_node_from_args( + this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD), + key_hash)), + true); + } + } + + template + iterator emplace_hint_unique( + c_iterator hint, no_key, BOOST_UNORDERED_EMPLACE_ARGS) + { + node_tmp b(boost::unordered::detail::func::construct_node_from_args( + this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD), + this->node_alloc()); + const_key_type& k = this->get_key(b.node_); + if (hint.node_ && this->key_eq()(k, this->get_key(hint.node_))) { + return iterator(hint.node_); + } + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + if (pos) { + return iterator(pos); + } else { + return iterator( + this->resize_and_add_node_unique(b.release(), key_hash)); + } + } + + template + emplace_return emplace_unique(no_key, BOOST_UNORDERED_EMPLACE_ARGS) + { + node_tmp b(boost::unordered::detail::func::construct_node_from_args( + this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD), + this->node_alloc()); + const_key_type& k = this->get_key(b.node_); + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + if (pos) { + return emplace_return(iterator(pos), false); + } else { + return emplace_return(iterator(this->resize_and_add_node_unique( + b.release(), key_hash)), + true); + } + } + + template + emplace_return try_emplace_unique(BOOST_FWD_REF(Key) k) + { + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + if (pos) { + return emplace_return(iterator(pos), false); + } else { + return emplace_return( + iterator(this->resize_and_add_node_unique( + boost::unordered::detail::func::construct_node_pair( + this->node_alloc(), boost::forward(k)), + key_hash)), + true); + } + } + + template + iterator try_emplace_hint_unique(c_iterator hint, BOOST_FWD_REF(Key) k) + { + if (hint.node_ && this->key_eq()(hint->first, k)) { + return iterator(hint.node_); + } else { + return try_emplace_unique(k).first; + } + } + + template + emplace_return try_emplace_unique( + BOOST_FWD_REF(Key) k, BOOST_UNORDERED_EMPLACE_ARGS) + { + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + if (pos) { + return emplace_return(iterator(pos), false); + } else { + return emplace_return( + iterator(this->resize_and_add_node_unique( + boost::unordered::detail::func:: + construct_node_pair_from_args(this->node_alloc(), + boost::forward(k), + BOOST_UNORDERED_EMPLACE_FORWARD), + key_hash)), + true); + } + } + + template + iterator try_emplace_hint_unique( + c_iterator hint, BOOST_FWD_REF(Key) k, BOOST_UNORDERED_EMPLACE_ARGS) + { + if (hint.node_ && this->key_eq()(hint->first, k)) { + return iterator(hint.node_); + } else { + return try_emplace_unique(k, BOOST_UNORDERED_EMPLACE_FORWARD).first; + } + } + + template + emplace_return insert_or_assign_unique( + BOOST_FWD_REF(Key) k, BOOST_FWD_REF(M) obj) + { + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + + if (pos) { + pos->value().second = boost::forward(obj); + return emplace_return(iterator(pos), false); + } else { + return emplace_return( + iterator(this->resize_and_add_node_unique( + boost::unordered::detail::func::construct_node_pair( + this->node_alloc(), boost::forward(k), + boost::forward(obj)), + key_hash)), + true); + } + } + + template + void move_insert_node_type_unique(NodeType& np, InsertReturnType& result) + { + if (np) { + const_key_type& k = this->get_key(np.ptr_); + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + + if (pos) { + result.node = boost::move(np); + result.position = iterator(pos); + } else { + this->reserve_for_insert(this->size_ + 1); + result.position = + iterator(this->add_node_unique(np.ptr_, key_hash)); + result.inserted = true; + np.ptr_ = node_pointer(); + } + } + } + + template + iterator move_insert_node_type_with_hint_unique( + c_iterator hint, NodeType& np) + { + if (!np) { + return iterator(); + } + const_key_type& k = this->get_key(np.ptr_); + if (hint.node_ && this->key_eq()(k, this->get_key(hint.node_))) { + return iterator(hint.node_); + } + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + if (!pos) { + this->reserve_for_insert(this->size_ + 1); + pos = this->add_node_unique(np.ptr_, key_hash); + np.ptr_ = node_pointer(); + } + return iterator(pos); + } + + template + void merge_unique(boost::unordered::detail::table& other) + { + typedef boost::unordered::detail::table other_table; + BOOST_STATIC_ASSERT( + (boost::is_same::value)); + BOOST_ASSERT(this->node_alloc() == other.node_alloc()); + + if (other.size_) { + link_pointer prev = other.get_previous_start(); + + while (prev->next_) { + node_pointer n = other_table::next_node(prev); + const_key_type& k = this->get_key(n); + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + + if (pos) { + prev = n; + } else { + this->reserve_for_insert(this->size_ + 1); + node_pointer n2 = next_node(n); + prev->next_ = n2; + if (n2 && n->is_first_in_group()) { + n2->set_first_in_group(); + } + --other.size_; + other.fix_bucket(other.node_bucket(n), prev, n2); + this->add_node_unique(n, key_hash); + } + } + } + } + + //////////////////////////////////////////////////////////////////////// + // Insert range methods + // + // if hash function throws, or inserting > 1 element, basic exception + // safety strong otherwise + + template + void insert_range_unique(const_key_type& k, InputIt i, InputIt j) + { + insert_range_unique2(k, i, j); + + while (++i != j) { + // Note: can't use get_key as '*i' might not be value_type - it + // could be a pair with first_types as key_type without const or + // a different second_type. + insert_range_unique2(extractor::extract(*i), i, j); + } + } + + template + void insert_range_unique2(const_key_type& k, InputIt i, InputIt j) + { + // No side effects in this initial code + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + + if (!pos) { + node_tmp b(boost::unordered::detail::func::construct_node( + this->node_alloc(), *i), + this->node_alloc()); + if (this->size_ + 1 > this->max_load_) + this->reserve_for_insert( + this->size_ + boost::unordered::detail::insert_size(i, j)); + this->add_node_unique(b.release(), key_hash); + } + } + + template + void insert_range_unique(no_key, InputIt i, InputIt j) + { + node_constructor a(this->node_alloc()); + + do { + if (!a.node_) { + a.create_node(); + } + 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_); + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + + if (pos) { + a.reclaim(b.release()); + } else { + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + this->reserve_for_insert(this->size_ + 1); + this->add_node_unique(b.release(), key_hash); + } + } while (++i != j); + } + + //////////////////////////////////////////////////////////////////////// + // Extract + + inline node_pointer extract_by_iterator_unique(c_iterator i) + { + node_pointer n = i.node_; + BOOST_ASSERT(n); + std::size_t bucket_index = this->node_bucket(n); + link_pointer prev = this->get_previous_start(bucket_index); + while (prev->next_ != n) { + prev = prev->next_; + } + node_pointer n2 = next_node(n); + prev->next_ = n2; + --this->size_; + this->fix_bucket(bucket_index, prev, n2); + n->next_ = link_pointer(); + return n; + } + + //////////////////////////////////////////////////////////////////////// + // Erase + // + // no throw + + std::size_t erase_key_unique(const_key_type& k) + { + if (!this->size_) + return 0; + std::size_t key_hash = this->hash(k); + std::size_t bucket_index = this->hash_to_bucket(key_hash); + link_pointer prev = this->find_previous_node(k, bucket_index); + if (!prev) + return 0; + node_pointer n = next_node(prev); + node_pointer n2 = next_node(n); + prev->next_ = n2; + --size_; + this->fix_bucket(bucket_index, prev, n2); + this->destroy_node(n); + return 1; + } + + void erase_nodes_unique(node_pointer i, node_pointer j) + { + std::size_t bucket_index = this->node_bucket(i); + + // Find the node before i. + link_pointer prev = this->get_previous_start(bucket_index); + while (prev->next_ != i) + prev = prev->next_; + + // Delete the nodes. + prev->next_ = j; + do { + node_pointer next = next_node(i); + destroy_node(i); + --size_; + bucket_index = this->fix_bucket(bucket_index, prev, next); + i = next; + } while (i != j); + } + + //////////////////////////////////////////////////////////////////////// + // fill_buckets_unique + + void copy_buckets(table const& src, true_type) + { + this->create_buckets(this->bucket_count_); + + for (node_pointer n = src.begin(); n; n = next_node(n)) { + std::size_t key_hash = this->hash(this->get_key(n)); + this->add_node_unique( + boost::unordered::detail::func::construct_node( + this->node_alloc(), n->value()), + key_hash); + } + } + + // TODO: Should be move_buckets_uniq + void move_buckets(table const& src) + { + this->create_buckets(this->bucket_count_); + + for (node_pointer n = src.begin(); n; n = next_node(n)) { + std::size_t key_hash = this->hash(this->get_key(n)); + this->add_node_unique( + boost::unordered::detail::func::construct_node( + this->node_alloc(), boost::move(n->value())), + key_hash); + } + } + + void assign_buckets(table const& src, true_type) + { + node_holder holder(*this); + for (node_pointer n = src.begin(); n; n = next_node(n)) { + std::size_t key_hash = this->hash(this->get_key(n)); + this->add_node_unique(holder.copy_of(n->value()), key_hash); + } + } + + void move_assign_buckets(table& src, true_type) + { + node_holder holder(*this); + for (node_pointer n = src.begin(); n; n = next_node(n)) { + std::size_t key_hash = this->hash(this->get_key(n)); + this->add_node_unique(holder.move_copy_of(n->value()), key_hash); + } + } + + //////////////////////////////////////////////////////////////////////// + // Equivalent keys + + // Equality + + bool equals_equiv(table const& other) const + { + if (this->size_ != other.size_) + return false; + + for (node_pointer n1 = this->begin(); n1;) { + node_pointer n2 = other.find_node(other.get_key(n1)); + if (!n2) + return false; + node_pointer end1 = next_group(n1); + node_pointer end2 = next_group(n2); + if (!group_equals_equiv(n1, end1, n2, end2)) + return false; + n1 = end1; + } + + return true; + } + + static bool group_equals_equiv( + node_pointer n1, node_pointer end1, node_pointer n2, node_pointer end2) + { + for (;;) { + if (n1->value() != n2->value()) + break; + + n1 = next_node(n1); + n2 = next_node(n2); + + if (n1 == end1) + return n2 == end2; + if (n2 == end2) + return false; + } + + for (node_pointer n1a = n1, n2a = n2;;) { + n1a = next_node(n1a); + n2a = next_node(n2a); + + if (n1a == end1) { + if (n2a == end2) + break; + else + return false; + } + + if (n2a == end2) + return false; + } + + node_pointer start = n1; + for (; n1 != end1; n1 = next_node(n1)) { + value_type const& v = n1->value(); + if (!find_equiv(start, n1, v)) { + std::size_t matches = count_equal_equiv(n2, end2, v); + if (!matches) + return false; + if (matches != 1 + count_equal_equiv(next_node(n1), end1, v)) + return false; + } + } + + return true; + } + + static bool find_equiv( + node_pointer n, node_pointer end, value_type const& v) + { + for (; n != end; n = next_node(n)) + if (n->value() == v) + return true; + return false; + } + + static std::size_t count_equal_equiv( + node_pointer n, node_pointer end, value_type const& v) + { + std::size_t count = 0; + for (; n != end; n = next_node(n)) + if (n->value() == v) + ++count; + return count; + } + + // Emplace/Insert + + inline node_pointer add_node_equiv( + node_pointer n, std::size_t key_hash, node_pointer pos) + { + std::size_t bucket_index = this->hash_to_bucket(key_hash); + n->bucket_info_ = bucket_index; + + if (pos) { + n->reset_first_in_group(); + n->next_ = pos->next_; + pos->next_ = n; + if (n->next_) { + std::size_t next_bucket = this->node_bucket(next_node(n)); + if (next_bucket != bucket_index) { + this->get_bucket(next_bucket)->next_ = n; + } + } + } else { + // n->set_first_in_group(); + bucket_pointer b = this->get_bucket(bucket_index); + + if (!b->next_) { + link_pointer start_node = this->get_previous_start(); + + if (start_node->next_) { + this->get_bucket(this->node_bucket(next_node(start_node))) + ->next_ = n; + } + + b->next_ = start_node; + n->next_ = start_node->next_; + start_node->next_ = n; + } else { + n->next_ = b->next_->next_; + b->next_->next_ = n; + } + } + ++this->size_; + return n; + } + + inline node_pointer add_using_hint_equiv(node_pointer n, node_pointer hint) + { + n->bucket_info_ = hint->bucket_info_; + n->reset_first_in_group(); + n->next_ = hint->next_; + hint->next_ = n; + if (n->next_) { + std::size_t next_bucket = this->node_bucket(next_node(n)); + if (next_bucket != this->node_bucket(n)) { + this->get_bucket(next_bucket)->next_ = n; + } + } + ++this->size_; + return n; + } + + iterator emplace_equiv(node_pointer n) + { + node_tmp a(n, this->node_alloc()); + const_key_type& k = this->get_key(a.node_); + std::size_t key_hash = this->hash(k); + node_pointer position = this->find_node(key_hash, k); + this->reserve_for_insert(this->size_ + 1); + return iterator(this->add_node_equiv(a.release(), key_hash, position)); + } + + iterator emplace_hint_equiv(c_iterator hint, node_pointer n) + { + node_tmp a(n, this->node_alloc()); + const_key_type& k = this->get_key(a.node_); + if (hint.node_ && this->key_eq()(k, this->get_key(hint.node_))) { + this->reserve_for_insert(this->size_ + 1); + return iterator( + this->add_using_hint_equiv(a.release(), hint.node_)); + } else { + std::size_t key_hash = this->hash(k); + node_pointer position = this->find_node(key_hash, k); + this->reserve_for_insert(this->size_ + 1); + return iterator( + this->add_node_equiv(a.release(), key_hash, position)); + } + } + + void emplace_no_rehash_equiv(node_pointer n) + { + node_tmp a(n, this->node_alloc()); + const_key_type& k = this->get_key(a.node_); + std::size_t key_hash = this->hash(k); + node_pointer position = this->find_node(key_hash, k); + this->add_node_equiv(a.release(), key_hash, position); + } + + template + iterator move_insert_node_type_equiv(NodeType& np) + { + iterator result; + + if (np) { + const_key_type& k = this->get_key(np.ptr_); + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + this->reserve_for_insert(this->size_ + 1); + result = iterator(this->add_node_equiv(np.ptr_, key_hash, pos)); + np.ptr_ = node_pointer(); + } + + return result; + } + + template + iterator move_insert_node_type_with_hint_equiv( + c_iterator hint, NodeType& np) + { + iterator result; + + if (np) { + const_key_type& k = this->get_key(np.ptr_); + + if (hint.node_ && this->key_eq()(k, this->get_key(hint.node_))) { + this->reserve_for_insert(this->size_ + 1); + result = + iterator(this->add_using_hint_equiv(np.ptr_, hint.node_)); + } else { + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + this->reserve_for_insert(this->size_ + 1); + result = iterator(this->add_node_equiv(np.ptr_, key_hash, pos)); + } + np.ptr_ = node_pointer(); + } + + return result; + } + + //////////////////////////////////////////////////////////////////////// + // Insert range methods + + // if hash function throws, or inserting > 1 element, basic exception + // safety. Strong otherwise + template + void insert_range_equiv(I i, I j, + typename boost::unordered::detail::enable_if_forward::type = + 0) + { + if (i == j) + return; + + std::size_t distance = static_cast(std::distance(i, j)); + if (distance == 1) { + emplace_equiv(boost::unordered::detail::func::construct_node( + this->node_alloc(), *i)); + } else { + // Only require basic exception safety here + this->reserve_for_insert(this->size_ + distance); + + for (; i != j; ++i) { + emplace_no_rehash_equiv( + boost::unordered::detail::func::construct_node( + this->node_alloc(), *i)); + } + } + } + + template + void insert_range_equiv(I i, I j, + typename boost::unordered::detail::disable_if_forward::type = + 0) + { + for (; i != j; ++i) { + emplace_equiv(boost::unordered::detail::func::construct_node( + this->node_alloc(), *i)); + } + } + + //////////////////////////////////////////////////////////////////////// + // Extract + + inline node_pointer extract_by_iterator_equiv(c_iterator n) + { + node_pointer i = n.node_; + BOOST_ASSERT(i); + node_pointer j(next_node(i)); + std::size_t bucket_index = this->node_bucket(i); + + link_pointer prev = this->get_previous_start(bucket_index); + while (prev->next_ != i) { + prev = next_node(prev); + } + + prev->next_ = j; + if (j && i->is_first_in_group()) { + j->set_first_in_group(); + } + --this->size_; + this->fix_bucket(bucket_index, prev, j); + i->next_ = link_pointer(); + + return i; + } + + //////////////////////////////////////////////////////////////////////// + // Erase + // + // no throw + + std::size_t erase_key_equiv(const_key_type& k) + { + if (!this->size_) + return 0; + + std::size_t key_hash = this->hash(k); + std::size_t bucket_index = this->hash_to_bucket(key_hash); + link_pointer prev = this->find_previous_node(k, bucket_index); + if (!prev) + return 0; + + std::size_t deleted_count = 0; + node_pointer n = next_node(prev); + do { + node_pointer n2 = next_node(n); + destroy_node(n); + ++deleted_count; + n = n2; + } while (n && !n->is_first_in_group()); + size_ -= deleted_count; + prev->next_ = n; + this->fix_bucket(bucket_index, prev, n); + return deleted_count; + } + + link_pointer erase_nodes_equiv(node_pointer i, node_pointer j) + { + std::size_t bucket_index = this->node_bucket(i); + + link_pointer prev = this->get_previous_start(bucket_index); + while (prev->next_ != i) { + prev = next_node(prev); + } + + // Delete the nodes. + // Is it inefficient to call fix_bucket for every node? + bool includes_first = false; + prev->next_ = j; + do { + includes_first = includes_first || i->is_first_in_group(); + node_pointer next = next_node(i); + destroy_node(i); + --size_; + bucket_index = this->fix_bucket(bucket_index, prev, next); + i = next; + } while (i != j); + if (j && includes_first) { + j->set_first_in_group(); + } + + return prev; + } + + //////////////////////////////////////////////////////////////////////// + // fill_buckets + + void copy_buckets(table const& src, false_type) + { + this->create_buckets(this->bucket_count_); + + for (node_pointer n = src.begin(); n;) { + std::size_t key_hash = this->hash(this->get_key(n)); + node_pointer group_end(next_group(n)); + node_pointer pos = this->add_node_equiv( + boost::unordered::detail::func::construct_node( + this->node_alloc(), n->value()), + key_hash, node_pointer()); + for (n = next_node(n); n != group_end; n = next_node(n)) { + this->add_node_equiv( + boost::unordered::detail::func::construct_node( + this->node_alloc(), n->value()), + key_hash, pos); + } + } + } + + void move_buckets_equiv(table const& src) + { + this->create_buckets(this->bucket_count_); + + for (node_pointer n = src.begin(); n;) { + std::size_t key_hash = this->hash(this->get_key(n)); + node_pointer group_end(next_group(n)); + node_pointer pos = this->add_node_equiv( + boost::unordered::detail::func::construct_node( + this->node_alloc(), boost::move(n->value())), + key_hash, node_pointer()); + for (n = next_node(n); n != group_end; n = next_node(n)) { + this->add_node_equiv( + boost::unordered::detail::func::construct_node( + this->node_alloc(), boost::move(n->value())), + key_hash, pos); + } + } + } + + void assign_buckets(table const& src, false_type) + { + node_holder holder(*this); + for (node_pointer n = src.begin(); n;) { + std::size_t key_hash = this->hash(this->get_key(n)); + node_pointer group_end(next_group(n)); + node_pointer pos = this->add_node_equiv( + holder.copy_of(n->value()), key_hash, node_pointer()); + for (n = next_node(n); n != group_end; n = next_node(n)) { + this->add_node_equiv(holder.copy_of(n->value()), key_hash, pos); + } + } + } + + void move_assign_buckets(table& src, false_type) + { + node_holder holder(*this); + for (node_pointer n = src.begin(); n;) { + std::size_t key_hash = this->hash(this->get_key(n)); + node_pointer group_end(next_group(n)); + node_pointer pos = this->add_node_equiv( + holder.move_copy_of(n->value()), key_hash, node_pointer()); + for (n = next_node(n); n != group_end; n = next_node(n)) { + this->add_node_equiv( + holder.move_copy_of(n->value()), key_hash, pos); + } + } + } }; +//////////////////////////////////////////////////////////////////////////// +// Clear + +template inline void table::clear_impl() +{ + if (size_) { + bucket_pointer end = get_bucket(bucket_count_); + for (bucket_pointer it = buckets_; it != end; ++it) { + it->next_ = node_pointer(); + } + + link_pointer prev = end->first_from_start(); + node_pointer n = next_node(prev); + prev->next_ = node_pointer(); + size_ = 0; + + while (n) { + node_pointer next = next_node(n); + destroy_node(n); + n = next; + } + } +} + //////////////////////////////////////////////////////////////////////////// // Reserve & Rehash @@ -3245,13 +4282,6 @@ inline void table::rehash(std::size_t min_buckets) } } -template -inline void table::reserve(std::size_t num_elements) -{ - rehash(static_cast( - std::ceil(static_cast(num_elements) / mlf_))); -} - template inline void table::rehash_impl(std::size_t num_buckets) { @@ -3259,20 +4289,54 @@ inline void table::rehash_impl(std::size_t num_buckets) this->create_buckets(num_buckets); link_pointer prev = this->get_previous_start(); - while (prev->next_) { - node_pointer group_last = node_algo::last_for_rehash(prev); - bucket_pointer b = - this->get_bucket(this->hash_to_bucket(group_last->hash_)); - if (!b->next_) { - b->next_ = prev; - prev = group_last; - } else { - link_pointer next = group_last->next_; - group_last->next_ = b->next_->next_; - b->next_->next_ = prev->next_; - prev->next_ = next; + BOOST_TRY + { + while (prev->next_) { + node_pointer n = next_node(prev); + std::size_t key_hash = this->hash(this->get_key(n)); + std::size_t bucket_index = this->hash_to_bucket(key_hash); + + n->bucket_info_ = bucket_index; + n->set_first_in_group(); + + // Iterator through the rest of the group of equal nodes, + // setting the bucket. + for (;;) { + node_pointer next = next_node(n); + if (!next || next->is_first_in_group()) { + break; + } + n = next; + n->bucket_info_ = bucket_index; + n->reset_first_in_group(); + } + + // n is now the last node in the group + bucket_pointer b = this->get_bucket(bucket_index); + if (!b->next_) { + b->next_ = prev; + prev = n; + } else { + link_pointer next = n->next_; + n->next_ = b->next_->next_; + b->next_->next_ = prev->next_; + prev->next_ = next; + } } } + BOOST_CATCH(...) + { + node_pointer n = next_node(prev); + prev->next_ = node_pointer(); + while (n) { + node_pointer next = next_node(n); + destroy_node(n); + --size_; + n = next; + } + BOOST_RETHROW + } + BOOST_CATCH_END } #if defined(BOOST_MSVC) @@ -3290,12 +4354,6 @@ inline void table::rehash_impl(std::size_t num_buckets) // for the different cases, but that's a bit tricky on compilers without // variadic templates. -struct no_key -{ - no_key() {} - template no_key(T const&) {} -}; - template struct is_key { template static choice1::type test(T2 const&); @@ -3318,6 +4376,11 @@ template struct set_extractor static key_type const& extract(value_type const& v) { return v; } + static key_type const& extract(BOOST_UNORDERED_RV_REF(value_type) v) + { + return v; + } + static no_key extract() { return no_key(); } template static no_key extract(Arg const&) { return no_key(); } @@ -3357,6 +4420,22 @@ template struct map_extractor return v.first; } +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + static key_type const& extract( + boost::rv > const& v) + { + return v.first; + } + + template + static key_type const& extract( + boost::rv > const& v) + { + return v.first; + } +#endif + template static key_type const& extract(key_type const& k, Arg1 const&) { @@ -3419,19 +4498,21 @@ template struct map_extractor BOOST_UNORDERED_KEY_FROM_TUPLE(boost::) -#if !defined(BOOST_NO_CXX11_HDR_TUPLE) +#if BOOST_UNORDERED_TUPLE_ARGS BOOST_UNORDERED_KEY_FROM_TUPLE(std::) #endif + +#undef BOOST_UNORDERED_KEY_FROM_TUPLE }; //////////////////////////////////////////////////////////////////////// // Unique nodes template -struct unique_node : boost::unordered::detail::value_base +struct node : boost::unordered::detail::value_base { typedef typename ::boost::unordered::detail::rebind_wrap >::type allocator; + node >::type allocator; typedef typename ::boost::unordered::detail::allocator_traits< allocator>::pointer node_pointer; typedef node_pointer link_pointer; @@ -3441,14 +4522,32 @@ struct unique_node : boost::unordered::detail::value_base bucket_allocator>::pointer bucket_pointer; link_pointer next_; - std::size_t hash_; + std::size_t bucket_info_; - unique_node() : next_(), hash_(0) {} + node() : next_(), bucket_info_(0) {} - void init(node_pointer) {} + std::size_t get_bucket() const + { + return bucket_info_ & ((std::size_t)-1 >> 1); + } + + std::size_t is_first_in_group() const + { + return !(bucket_info_ & ~((std::size_t)-1 >> 1)); + } + + void set_first_in_group() + { + bucket_info_ = bucket_info_ & ((std::size_t)-1 >> 1); + } + + void reset_first_in_group() + { + bucket_info_ = bucket_info_ | ~((std::size_t)-1 >> 1); + } private: - unique_node& operator=(unique_node const&); + node& operator=(node const&); }; template struct ptr_node : boost::unordered::detail::ptr_bucket @@ -3459,111 +4558,46 @@ template struct ptr_node : boost::unordered::detail::ptr_bucket typedef ptr_bucket* link_pointer; typedef ptr_bucket* bucket_pointer; - std::size_t hash_; + std::size_t bucket_info_; boost::unordered::detail::value_base value_base_; - ptr_node() : bucket_base(), hash_(0) {} - - void init(node_pointer) {} + ptr_node() : bucket_base(), bucket_info_(0) {} void* address() { return value_base_.address(); } value_type& value() { return value_base_.value(); } value_type* value_ptr() { return value_base_.value_ptr(); } + std::size_t get_bucket() const + { + return bucket_info_ & ((std::size_t)-1 >> 1); + } + + std::size_t is_first_in_group() const + { + return !(bucket_info_ & ~((std::size_t)-1 >> 1)); + } + + void set_first_in_group() + { + bucket_info_ = bucket_info_ & ((std::size_t)-1 >> 1); + } + + void reset_first_in_group() + { + bucket_info_ = bucket_info_ | ~((std::size_t)-1 >> 1); + } + private: ptr_node& operator=(ptr_node const&); }; -template struct node_algo -{ - typedef typename N::node_pointer node_pointer; - typedef typename N::link_pointer link_pointer; - typedef typename N::bucket_pointer bucket_pointer; - - static node_pointer next_node(link_pointer n) - { - return static_cast(n->next_); - } - - static node_pointer next_for_find(node_pointer n) - { - return static_cast(n->next_); - } - - static link_pointer next_for_erase(link_pointer prev) - { - return prev->next_; - } - - // Group together all nodes with equal hash value, this may - // include nodes with different keys, but that's okay because - // they will end up in the same bucket. - static node_pointer last_for_rehash(link_pointer prev) - { - node_pointer n = next_node(prev); - std::size_t hash = n->hash_; - for (;;) { - node_pointer next = next_node(n); - if (!next || next->hash_ != hash) { - return n; - } - n = next; - } - } - - template - static node_pointer next_group(node_pointer n, Table const* t) - { - node_pointer n1 = n; - do { - n1 = next_node(n1); - } while ( - n1 && t->key_eq()(t->get_key(n->value()), t->get_key(n1->value()))); - return n1; - } - - template - static std::size_t count(node_pointer n, Table const* t) - { - std::size_t x = 0; - node_pointer it = n; - do { - ++x; - it = next_node(it); - } while ( - it && t->key_eq()(t->get_key(n->value()), t->get_key(it->value()))); - - return x; - } - - // Add node 'n' after 'pos'. - // This results in a different order to the grouped implementation. - static inline void add_to_node_group(node_pointer n, node_pointer pos) - { - n->next_ = pos->next_; - pos->next_ = n; - } - - static inline node_pointer extract_first_node(link_pointer prev) - { - node_pointer n = next_node(prev); - prev->next_ = n->next_; - return n; - } - - static link_pointer split_groups(node_pointer, node_pointer) - { - return link_pointer(); - } -}; - // If the allocator uses raw pointers use ptr_node // Otherwise use node. template struct pick_node2 { - typedef boost::unordered::detail::unique_node node; + typedef boost::unordered::detail::node node; typedef typename boost::unordered::detail::allocator_traits< typename boost::unordered::detail::rebind_wrap::type>::pointer @@ -3603,1384 +4637,15 @@ template struct pick_node typedef typename pick::node node; typedef typename pick::bucket bucket; typedef typename pick::link_pointer link_pointer; - typedef boost::unordered::detail::node_algo node_algo; -}; - -template -struct table_impl : boost::unordered::detail::table -{ - typedef boost::unordered::detail::table table; - typedef typename table::value_type value_type; - typedef typename table::node node; - typedef typename table::bucket bucket; - typedef typename table::policy policy; - typedef typename table::node_pointer node_pointer; - typedef typename table::node_allocator node_allocator; - typedef typename table::node_allocator_traits node_allocator_traits; - typedef typename table::bucket_pointer bucket_pointer; - typedef typename table::link_pointer link_pointer; - typedef typename table::hasher hasher; - typedef typename table::key_equal key_equal; - typedef typename table::const_key_type const_key_type; - typedef typename table::node_constructor node_constructor; - typedef typename table::node_tmp node_tmp; - typedef typename table::extractor extractor; - typedef typename table::iterator iterator; - typedef typename table::c_iterator c_iterator; - typedef typename table::node_algo node_algo; - - typedef std::pair emplace_return; - - // Constructors - - table_impl(std::size_t n, hasher const& hf, key_equal const& eq, - node_allocator const& a) - : table(n, hf, eq, a) - { - } - - table_impl(table_impl const& x) - : table(x, node_allocator_traits::select_on_container_copy_construction( - x.node_alloc())) - { - this->init(x); - } - - table_impl(table_impl const& x, node_allocator const& a) : table(x, a) - { - this->init(x); - } - - table_impl(table_impl& x, boost::unordered::detail::move_tag m) - : table(x, m) - { - } - - table_impl(table_impl& x, node_allocator const& a, - boost::unordered::detail::move_tag m) - : table(x, a, m) - { - this->move_init(x); - } - - // Accessors - - std::size_t count(const_key_type& k) const - { - return this->find_node(k) ? 1 : 0; - } - - value_type& at(const_key_type& k) const - { - if (this->size_) { - node_pointer n = this->find_node(k); - if (n) - return n->value(); - } - - boost::throw_exception( - std::out_of_range("Unable to find key in unordered_map.")); - } - - std::pair equal_range(const_key_type& k) const - { - node_pointer n = this->find_node(k); - return std::make_pair( - iterator(n), iterator(n ? node_algo::next_node(n) : n)); - } - - // equals - - bool equals(table_impl const& other) const - { - if (this->size_ != other.size_) - return false; - - for (node_pointer n1 = this->begin(); n1; - n1 = node_algo::next_node(n1)) { - node_pointer n2 = other.find_node(other.get_key(n1->value())); - - if (!n2 || n1->value() != n2->value()) - return false; - } - - return true; - } - - // Emplace/Insert - - inline node_pointer add_node(node_pointer n, std::size_t key_hash) - { - n->hash_ = key_hash; - - bucket_pointer b = this->get_bucket(this->hash_to_bucket(key_hash)); - - if (!b->next_) { - link_pointer start_node = this->get_previous_start(); - - if (start_node->next_) { - this->get_bucket(this->hash_to_bucket( - node_algo::next_node(start_node)->hash_)) - ->next_ = n; - } - - b->next_ = start_node; - n->next_ = start_node->next_; - start_node->next_ = n; - } else { - n->next_ = b->next_->next_; - b->next_->next_ = n; - } - - ++this->size_; - return n; - } - - inline node_pointer resize_and_add_node( - node_pointer n, std::size_t key_hash) - { - node_tmp b(n, this->node_alloc()); - this->reserve_for_insert(this->size_ + 1); - return this->add_node(b.release(), key_hash); - } - -#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) -#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) - emplace_return emplace(boost::unordered::detail::emplace_args1< - boost::unordered::detail::please_ignore_this_overload> const&) - { - BOOST_ASSERT(false); - return emplace_return(iterator(), false); - } - - iterator emplace_hint( - c_iterator, - boost::unordered::detail::emplace_args1< - boost::unordered::detail::please_ignore_this_overload> const&) - { - BOOST_ASSERT(false); - return iterator(); - } -#else - emplace_return emplace( - boost::unordered::detail::please_ignore_this_overload const&) - { - BOOST_ASSERT(false); - return emplace_return(iterator(), false); - } - - iterator emplace_hint(c_iterator, - boost::unordered::detail::please_ignore_this_overload const&) - { - BOOST_ASSERT(false); - return iterator(); - } -#endif -#endif - - template - emplace_return emplace(BOOST_UNORDERED_EMPLACE_ARGS) - { -#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) - return emplace_impl(extractor::extract(BOOST_UNORDERED_EMPLACE_FORWARD), - BOOST_UNORDERED_EMPLACE_FORWARD); -#else - return emplace_impl(extractor::extract(args.a0, args.a1), - BOOST_UNORDERED_EMPLACE_FORWARD); -#endif - } - - template - iterator emplace_hint(c_iterator hint, BOOST_UNORDERED_EMPLACE_ARGS) - { -#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) - return emplace_hint_impl(hint, - extractor::extract(BOOST_UNORDERED_EMPLACE_FORWARD), - BOOST_UNORDERED_EMPLACE_FORWARD); -#else - return emplace_hint_impl(hint, extractor::extract(args.a0, args.a1), - BOOST_UNORDERED_EMPLACE_FORWARD); -#endif - } - -#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) - template - emplace_return emplace( - boost::unordered::detail::emplace_args1 const& args) - { - return emplace_impl(extractor::extract(args.a0), args); - } - - template - iterator emplace_hint(c_iterator hint, - boost::unordered::detail::emplace_args1 const& args) - { - return emplace_hint_impl(hint, extractor::extract(args.a0), args); - } -#endif - - template - iterator emplace_hint_impl( - c_iterator hint, const_key_type& k, BOOST_UNORDERED_EMPLACE_ARGS) - { - if (hint.node_ && this->key_eq()(k, this->get_key(*hint))) { - return iterator(hint.node_); - } else { - return emplace_impl(k, BOOST_UNORDERED_EMPLACE_FORWARD).first; - } - } - - template - emplace_return emplace_impl(const_key_type& k, BOOST_UNORDERED_EMPLACE_ARGS) - { - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - if (pos) { - return emplace_return(iterator(pos), false); - } else { - return emplace_return( - iterator(this->resize_and_add_node( - boost::unordered::detail::func::construct_node_from_args( - this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD), - key_hash)), - true); - } - } - - template - iterator emplace_hint_impl( - c_iterator hint, no_key, BOOST_UNORDERED_EMPLACE_ARGS) - { - node_tmp b(boost::unordered::detail::func::construct_node_from_args( - this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD), - this->node_alloc()); - const_key_type& k = this->get_key(b.node_->value()); - if (hint.node_ && this->key_eq()(k, this->get_key(*hint))) { - return iterator(hint.node_); - } - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - if (pos) { - return iterator(pos); - } else { - return iterator(this->resize_and_add_node(b.release(), key_hash)); - } - } - - template - emplace_return emplace_impl(no_key, BOOST_UNORDERED_EMPLACE_ARGS) - { - node_tmp b(boost::unordered::detail::func::construct_node_from_args( - this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD), - this->node_alloc()); - const_key_type& k = this->get_key(b.node_->value()); - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - if (pos) { - return emplace_return(iterator(pos), false); - } else { - return emplace_return( - iterator(this->resize_and_add_node(b.release(), key_hash)), - true); - } - } - - template - emplace_return try_emplace_impl(BOOST_FWD_REF(Key) k) - { - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - if (pos) { - return emplace_return(iterator(pos), false); - } else { - return emplace_return( - iterator(this->resize_and_add_node( - boost::unordered::detail::func::construct_node_pair( - this->node_alloc(), boost::forward(k)), - key_hash)), - true); - } - } - - template - iterator try_emplace_hint_impl(c_iterator hint, BOOST_FWD_REF(Key) k) - { - if (hint.node_ && this->key_eq()(hint->first, k)) { - return iterator(hint.node_); - } else { - return try_emplace_impl(k).first; - } - } - - template - emplace_return try_emplace_impl( - BOOST_FWD_REF(Key) k, BOOST_UNORDERED_EMPLACE_ARGS) - { - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - if (pos) { - return emplace_return(iterator(pos), false); - } else { - return emplace_return( - iterator(this->resize_and_add_node( - boost::unordered::detail::func:: - construct_node_pair_from_args(this->node_alloc(), - boost::forward(k), - BOOST_UNORDERED_EMPLACE_FORWARD), - key_hash)), - true); - } - } - - template - iterator try_emplace_hint_impl( - c_iterator hint, BOOST_FWD_REF(Key) k, BOOST_UNORDERED_EMPLACE_ARGS) - { - if (hint.node_ && this->key_eq()(hint->first, k)) { - return iterator(hint.node_); - } else { - return try_emplace_impl(k, BOOST_UNORDERED_EMPLACE_FORWARD).first; - } - } - - template - emplace_return insert_or_assign_impl( - BOOST_FWD_REF(Key) k, BOOST_FWD_REF(M) obj) - { - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - - if (pos) { - pos->value().second = boost::forward(obj); - return emplace_return(iterator(pos), false); - } else { - return emplace_return( - iterator(this->resize_and_add_node( - boost::unordered::detail::func::construct_node_pair( - this->node_alloc(), boost::forward(k), - boost::forward(obj)), - key_hash)), - true); - } - } - - template - void move_insert_node_type(NodeType& np, InsertReturnType& result) - { - if (np) { - const_key_type& k = this->get_key(np.ptr_->value()); - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - - if (pos) { - result.node = boost::move(np); - result.position = iterator(pos); - } else { - this->reserve_for_insert(this->size_ + 1); - result.position = iterator(this->add_node(np.ptr_, key_hash)); - result.inserted = true; - np.ptr_ = node_pointer(); - } - } - } - - template - iterator move_insert_node_type_with_hint(c_iterator hint, NodeType& np) - { - if (!np) { - return iterator(); - } - const_key_type& k = this->get_key(np.ptr_->value()); - if (hint.node_ && this->key_eq()(k, this->get_key(*hint))) { - return iterator(hint.node_); - } - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - if (!pos) { - this->reserve_for_insert(this->size_ + 1); - pos = this->add_node(np.ptr_, key_hash); - np.ptr_ = node_pointer(); - } - return iterator(pos); - } - - template - void merge_impl(boost::unordered::detail::table& other) - { - typedef boost::unordered::detail::table other_table; - BOOST_STATIC_ASSERT( - (boost::is_same::value)); - BOOST_ASSERT(this->node_alloc() == other.node_alloc()); - - if (other.size_) { - link_pointer prev = other.get_previous_start(); - - while (prev->next_) { - node_pointer n = other_table::node_algo::next_node(prev); - const_key_type& k = this->get_key(n->value()); - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - - if (pos) { - prev = n; - } else { - this->reserve_for_insert(this->size_ + 1); - other_table::node_algo::split_groups( - n, other_table::node_algo::next_node(n)); - prev->next_ = n->next_; - --other.size_; - other.fix_bucket(other.hash_to_bucket(n->hash_), prev); - this->add_node(n, key_hash); - } - } - } - } - - //////////////////////////////////////////////////////////////////////// - // Insert range methods - // - // if hash function throws, or inserting > 1 element, basic exception - // safety strong otherwise - - template void insert_range(InputIt i, InputIt j) - { - if (i != j) - return insert_range_impl(extractor::extract(*i), i, j); - } - - template - void insert_range_impl(const_key_type& k, InputIt i, InputIt j) - { - insert_range_impl2(k, i, j); - - while (++i != j) { - // Note: can't use get_key as '*i' might not be value_type - it - // could be a pair with first_types as key_type without const or - // a different second_type. - // - // TODO: Might be worth storing the value_type instead of the - // key here. Could be more efficient if '*i' is expensive. Could - // be less efficient if copying the full value_type is - // expensive. - insert_range_impl2(extractor::extract(*i), i, j); - } - } - - template - void insert_range_impl2(const_key_type& k, InputIt i, InputIt j) - { - // No side effects in this initial code - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - - if (!pos) { - node_tmp b(boost::unordered::detail::func::construct_node( - this->node_alloc(), *i), - this->node_alloc()); - if (this->size_ + 1 > this->max_load_) - this->reserve_for_insert( - this->size_ + boost::unordered::detail::insert_size(i, j)); - this->add_node(b.release(), key_hash); - } - } - - template - void insert_range_impl(no_key, InputIt i, InputIt j) - { - node_constructor a(this->node_alloc()); - - do { - if (!a.node_) { - a.create_node(); - } - boost::unordered::detail::func::call_construct( - a.alloc_, a.node_->value_ptr(), *i); - node_tmp b(a.release(), a.alloc_); - - const_key_type& k = this->get_key(b.node_->value()); - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - - if (pos) { - a.reclaim(b.release()); - } else { - // reserve has basic exception safety if the hash function - // throws, strong otherwise. - this->reserve_for_insert(this->size_ + 1); - this->add_node(b.release(), key_hash); - } - } while (++i != j); - } - - //////////////////////////////////////////////////////////////////////// - // Extract - - inline node_pointer extract_by_iterator(c_iterator i) - { - node_pointer n = i.node_; - BOOST_ASSERT(n); - std::size_t key_hash = n->hash_; - std::size_t bucket_index = this->hash_to_bucket(key_hash); - link_pointer prev = this->get_previous_start(bucket_index); - while (prev->next_ != n) { - prev = prev->next_; - } - prev->next_ = n->next_; - --this->size_; - this->fix_bucket(bucket_index, prev); - n->next_ = link_pointer(); - return n; - } - - //////////////////////////////////////////////////////////////////////// - // Erase - // - // no throw - - std::size_t erase_key(const_key_type& k) - { - if (!this->size_) - return 0; - std::size_t key_hash = this->hash(k); - std::size_t bucket_index = this->hash_to_bucket(key_hash); - link_pointer prev = this->find_previous_node(k, key_hash, bucket_index); - if (!prev) - return 0; - link_pointer end = node_algo::next_node(prev)->next_; - this->delete_nodes(prev, end); - this->fix_bucket(bucket_index, prev); - return 1; - } - - iterator erase(c_iterator r) - { - BOOST_ASSERT(r.node_); - node_pointer next = node_algo::next_node(r.node_); - erase_nodes(r.node_, next); - return iterator(next); - } - - iterator erase_range(c_iterator r1, c_iterator r2) - { - if (r1 == r2) - return iterator(r2.node_); - erase_nodes(r1.node_, r2.node_); - return iterator(r2.node_); - } - - void erase_nodes(node_pointer i, node_pointer j) - { - std::size_t bucket_index = this->hash_to_bucket(i->hash_); - - // Find the node before i. - link_pointer prev = this->get_previous_start(bucket_index); - while (prev->next_ != i) - prev = prev->next_; - - // Delete the nodes. - do { - this->delete_node(prev); - bucket_index = this->fix_bucket(bucket_index, prev); - } while (prev->next_ != j); - } - - //////////////////////////////////////////////////////////////////////// - // fill_buckets - - void copy_buckets(table const& src) - { - this->create_buckets(this->bucket_count_); - - for (node_pointer n = src.begin(); n; n = node_algo::next_node(n)) { - this->add_node(boost::unordered::detail::func::construct_node( - this->node_alloc(), n->value()), - n->hash_); - } - } - - void move_buckets(table const& src) - { - this->create_buckets(this->bucket_count_); - - for (node_pointer n = src.begin(); n; n = node_algo::next_node(n)) { - this->add_node(boost::unordered::detail::func::construct_node( - this->node_alloc(), boost::move(n->value())), - n->hash_); - } - } - - void assign_buckets(table const& src) - { - node_holder holder(*this); - for (node_pointer n = src.begin(); n; n = node_algo::next_node(n)) { - this->add_node(holder.copy_of(n->value()), n->hash_); - } - } - - void move_assign_buckets(table& src) - { - node_holder holder(*this); - for (node_pointer n = src.begin(); n; n = node_algo::next_node(n)) { - this->add_node(holder.move_copy_of(n->value()), n->hash_); - } - } -}; - -//////////////////////////////////////////////////////////////////////// -// Grouped nodes - -template -struct grouped_node : boost::unordered::detail::value_base -{ - typedef typename ::boost::unordered::detail::rebind_wrap >::type allocator; - typedef typename ::boost::unordered::detail::allocator_traits< - allocator>::pointer node_pointer; - typedef node_pointer link_pointer; - typedef typename ::boost::unordered::detail::rebind_wrap >::type bucket_allocator; - typedef typename ::boost::unordered::detail::allocator_traits< - bucket_allocator>::pointer bucket_pointer; - - link_pointer next_; - node_pointer group_prev_; - std::size_t hash_; - - grouped_node() : next_(), group_prev_(), hash_(0) {} - - void init(node_pointer self) { group_prev_ = self; } - - private: - grouped_node& operator=(grouped_node const&); -}; - -template -struct grouped_ptr_node : boost::unordered::detail::ptr_bucket -{ - typedef T value_type; - typedef boost::unordered::detail::ptr_bucket bucket_base; - typedef grouped_ptr_node* node_pointer; - typedef ptr_bucket* link_pointer; - typedef ptr_bucket* bucket_pointer; - - node_pointer group_prev_; - std::size_t hash_; - boost::unordered::detail::value_base value_base_; - - grouped_ptr_node() : bucket_base(), group_prev_(0), hash_(0) {} - - void init(node_pointer self) { group_prev_ = self; } - - void* address() { return value_base_.address(); } - value_type& value() { return value_base_.value(); } - value_type* value_ptr() { return value_base_.value_ptr(); } - - private: - grouped_ptr_node& operator=(grouped_ptr_node const&); -}; - -template struct grouped_node_algo -{ - typedef typename N::node_pointer node_pointer; - typedef typename N::link_pointer link_pointer; - typedef typename N::bucket_pointer bucket_pointer; - - static node_pointer next_node(link_pointer n) - { - return static_cast(n->next_); - } - - static node_pointer next_for_find(node_pointer n) - { - return static_cast(n->group_prev_->next_); - } - - static link_pointer next_for_erase(link_pointer prev) - { - return static_cast(prev->next_)->group_prev_; - } - - static node_pointer last_for_rehash(link_pointer prev) - { - return static_cast(prev->next_)->group_prev_; - } - - // The 'void*' arguments are pointers to the table, which we - // will ignore, but without groups they could be used to - // access the various functions for dealing with values and keys. - static node_pointer next_group(node_pointer n, void const*) - { - return static_cast(n->group_prev_->next_); - } - - static std::size_t count(node_pointer n, void const*) - { - std::size_t x = 0; - node_pointer it = n; - do { - it = it->group_prev_; - ++x; - } while (it != n); - - return x; - } - - // Adds node 'n' to the group containing 'pos'. - // If 'pos' is the first node in group, add to the end of the group, - // otherwise add before 'pos'. Other versions will probably behave - // differently. - static inline void add_to_node_group(node_pointer n, node_pointer pos) - { - n->next_ = pos->group_prev_->next_; - n->group_prev_ = pos->group_prev_; - pos->group_prev_->next_ = n; - pos->group_prev_ = n; - } - - static inline node_pointer extract_first_node(link_pointer prev) - { - node_pointer n = next_node(prev); - if (n->group_prev_ != n) { - node_pointer next = next_node(n); - next->group_prev_ = n->group_prev_; - n->group_prev_ = n; - } - prev->next_ = n->next_; - return n; - } - - // Split the groups containing 'i' and 'j' so that they can - // be safely erased/extracted. - static link_pointer split_groups(node_pointer i, node_pointer j) - { - node_pointer prev = i->group_prev_; - if (prev->next_ != i) - prev = node_pointer(); - - if (j) { - node_pointer first = j; - while (first != i && first->group_prev_->next_ == first) { - first = first->group_prev_; - } - - boost::swap(first->group_prev_, j->group_prev_); - if (first == i) - return prev; - } - - if (prev) { - node_pointer first = prev; - while (first->group_prev_->next_ == first) { - first = first->group_prev_; - } - boost::swap(first->group_prev_, i->group_prev_); - } - - return prev; - } -}; - -// If the allocator uses raw pointers use grouped_ptr_node -// Otherwise use grouped_node. - -template -struct pick_grouped_node2 -{ - typedef boost::unordered::detail::grouped_node node; - - typedef typename boost::unordered::detail::allocator_traits< - typename boost::unordered::detail::rebind_wrap::type>::pointer - node_pointer; - - typedef boost::unordered::detail::bucket bucket; - typedef node_pointer link_pointer; -}; - -template -struct pick_grouped_node2*, - boost::unordered::detail::ptr_bucket*> -{ - typedef boost::unordered::detail::grouped_ptr_node node; - typedef boost::unordered::detail::ptr_bucket bucket; - typedef bucket* link_pointer; -}; - -template struct pick_grouped_node -{ - typedef typename boost::remove_const::type nonconst; - - typedef boost::unordered::detail::allocator_traits< - typename boost::unordered::detail::rebind_wrap >::type> - tentative_node_traits; - - typedef boost::unordered::detail::allocator_traits< - typename boost::unordered::detail::rebind_wrap::type> - tentative_bucket_traits; - - typedef pick_grouped_node2 - pick; - - typedef typename pick::node node; - typedef typename pick::bucket bucket; - typedef typename pick::link_pointer link_pointer; - typedef boost::unordered::detail::grouped_node_algo node_algo; -}; - -template -struct grouped_table_impl : boost::unordered::detail::table -{ - typedef boost::unordered::detail::table table; - typedef typename table::value_type value_type; - typedef typename table::bucket bucket; - typedef typename table::policy policy; - typedef typename table::node_pointer node_pointer; - typedef typename table::node_allocator node_allocator; - typedef typename table::node_allocator_traits node_allocator_traits; - typedef typename table::bucket_pointer bucket_pointer; - typedef typename table::link_pointer link_pointer; - typedef typename table::hasher hasher; - typedef typename table::key_equal key_equal; - typedef typename table::const_key_type const_key_type; - typedef typename table::node_constructor node_constructor; - typedef typename table::node_tmp node_tmp; - typedef typename table::extractor extractor; - typedef typename table::iterator iterator; - typedef typename table::c_iterator c_iterator; - typedef typename table::node_algo node_algo; - - // Constructors - - grouped_table_impl(std::size_t n, hasher const& hf, key_equal const& eq, - node_allocator const& a) - : table(n, hf, eq, a) - { - } - - grouped_table_impl(grouped_table_impl const& x) - : table(x, node_allocator_traits::select_on_container_copy_construction( - x.node_alloc())) - { - this->init(x); - } - - grouped_table_impl(grouped_table_impl const& x, node_allocator const& a) - : table(x, a) - { - this->init(x); - } - - grouped_table_impl( - grouped_table_impl& x, boost::unordered::detail::move_tag m) - : table(x, m) - { - } - - grouped_table_impl(grouped_table_impl& x, node_allocator const& a, - boost::unordered::detail::move_tag m) - : table(x, a, m) - { - this->move_init(x); - } - - // Accessors - - std::size_t count(const_key_type& k) const - { - node_pointer n = this->find_node(k); - return n ? node_algo::count(n, this) : 0; - } - - std::pair equal_range(const_key_type& k) const - { - node_pointer n = this->find_node(k); - return std::make_pair( - iterator(n), iterator(n ? node_algo::next_group(n, this) : n)); - } - - // Equality - - bool equals(grouped_table_impl const& other) const - { - if (this->size_ != other.size_) - return false; - - for (node_pointer n1 = this->begin(); n1;) { - node_pointer n2 = other.find_node(other.get_key(n1->value())); - if (!n2) - return false; - node_pointer end1 = node_algo::next_group(n1, this); - node_pointer end2 = node_algo::next_group(n2, this); - if (!group_equals(n1, end1, n2, end2)) - return false; - n1 = end1; - } - - return true; - } - - static bool group_equals( - node_pointer n1, node_pointer end1, node_pointer n2, node_pointer end2) - { - for (;;) { - if (n1->value() != n2->value()) - break; - - n1 = node_algo::next_node(n1); - n2 = node_algo::next_node(n2); - - if (n1 == end1) - return n2 == end2; - if (n2 == end2) - return false; - } - - for (node_pointer n1a = n1, n2a = n2;;) { - n1a = node_algo::next_node(n1a); - n2a = node_algo::next_node(n2a); - - if (n1a == end1) { - if (n2a == end2) - break; - else - return false; - } - - if (n2a == end2) - return false; - } - - node_pointer start = n1; - for (; n1 != end1; n1 = node_algo::next_node(n1)) { - value_type const& v = n1->value(); - if (!find(start, n1, v)) { - std::size_t matches = count_equal(n2, end2, v); - if (!matches) - return false; - if (matches != - 1 + count_equal(node_algo::next_node(n1), end1, v)) - return false; - } - } - - return true; - } - - static bool find(node_pointer n, node_pointer end, value_type const& v) - { - for (; n != end; n = node_algo::next_node(n)) - if (n->value() == v) - return true; - return false; - } - - static std::size_t count_equal( - node_pointer n, node_pointer end, value_type const& v) - { - std::size_t count = 0; - for (; n != end; n = node_algo::next_node(n)) - if (n->value() == v) - ++count; - return count; - } - - // Emplace/Insert - - inline node_pointer add_node( - node_pointer n, std::size_t key_hash, node_pointer pos) - { - n->hash_ = key_hash; - if (pos) { - node_algo::add_to_node_group(n, pos); - if (n->next_) { - std::size_t next_bucket = - this->hash_to_bucket(node_algo::next_node(n)->hash_); - if (next_bucket != this->hash_to_bucket(key_hash)) { - this->get_bucket(next_bucket)->next_ = n; - } - } - } else { - bucket_pointer b = this->get_bucket(this->hash_to_bucket(key_hash)); - - if (!b->next_) { - link_pointer start_node = this->get_previous_start(); - - if (start_node->next_) { - this->get_bucket( - this->hash_to_bucket( - node_algo::next_node(start_node)->hash_)) - ->next_ = n; - } - - b->next_ = start_node; - n->next_ = start_node->next_; - start_node->next_ = n; - } else { - n->next_ = b->next_->next_; - b->next_->next_ = n; - } - } - ++this->size_; - return n; - } - - inline node_pointer add_using_hint(node_pointer n, node_pointer hint) - { - n->hash_ = hint->hash_; - node_algo::add_to_node_group(n, hint); - if (n->next_ != hint && n->next_) { - std::size_t next_bucket = - this->hash_to_bucket(node_algo::next_node(n)->hash_); - if (next_bucket != this->hash_to_bucket(n->hash_)) { - this->get_bucket(next_bucket)->next_ = n; - } - } - ++this->size_; - return n; - } - -#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) -#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) - iterator emplace(boost::unordered::detail::emplace_args1< - boost::unordered::detail::please_ignore_this_overload> const&) - { - BOOST_ASSERT(false); - return iterator(); - } - - iterator emplace_hint( - c_iterator, - boost::unordered::detail::emplace_args1< - boost::unordered::detail::please_ignore_this_overload> const&) - { - BOOST_ASSERT(false); - return iterator(); - } -#else - iterator emplace( - boost::unordered::detail::please_ignore_this_overload const&) - { - BOOST_ASSERT(false); - return iterator(); - } - - iterator emplace_hint(c_iterator, - boost::unordered::detail::please_ignore_this_overload const&) - { - BOOST_ASSERT(false); - return iterator(); - } -#endif -#endif - - template - iterator emplace(BOOST_UNORDERED_EMPLACE_ARGS) - { - return iterator(emplace_impl( - boost::unordered::detail::func::construct_node_from_args( - this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD))); - } - - template - iterator emplace_hint(c_iterator hint, BOOST_UNORDERED_EMPLACE_ARGS) - { - return iterator(emplace_hint_impl( - hint, boost::unordered::detail::func::construct_node_from_args( - this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD))); - } - - iterator emplace_impl(node_pointer n) - { - node_tmp a(n, this->node_alloc()); - const_key_type& k = this->get_key(a.node_->value()); - std::size_t key_hash = this->hash(k); - node_pointer position = this->find_node(key_hash, k); - this->reserve_for_insert(this->size_ + 1); - return iterator(this->add_node(a.release(), key_hash, position)); - } - - iterator emplace_hint_impl(c_iterator hint, node_pointer n) - { - node_tmp a(n, this->node_alloc()); - const_key_type& k = this->get_key(a.node_->value()); - if (hint.node_ && this->key_eq()(k, this->get_key(*hint))) { - this->reserve_for_insert(this->size_ + 1); - return iterator(this->add_using_hint(a.release(), hint.node_)); - } else { - std::size_t key_hash = this->hash(k); - node_pointer position = this->find_node(key_hash, k); - this->reserve_for_insert(this->size_ + 1); - return iterator(this->add_node(a.release(), key_hash, position)); - } - } - - void emplace_impl_no_rehash(node_pointer n) - { - node_tmp a(n, this->node_alloc()); - const_key_type& k = this->get_key(a.node_->value()); - std::size_t key_hash = this->hash(k); - node_pointer position = this->find_node(key_hash, k); - this->add_node(a.release(), key_hash, position); - } - - template iterator move_insert_node_type(NodeType& np) - { - iterator result; - - if (np) { - const_key_type& k = this->get_key(np.ptr_->value()); - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - this->reserve_for_insert(this->size_ + 1); - result = iterator(this->add_node(np.ptr_, key_hash, pos)); - np.ptr_ = node_pointer(); - } - - return result; - } - - template - iterator move_insert_node_type_with_hint(c_iterator hint, NodeType& np) - { - iterator result; - - if (np) { - const_key_type& k = this->get_key(np.ptr_->value()); - - if (hint.node_ && this->key_eq()(k, this->get_key(*hint))) { - this->reserve_for_insert(this->size_ + 1); - result = iterator(this->add_using_hint(np.ptr_, hint.node_)); - } else { - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - this->reserve_for_insert(this->size_ + 1); - result = iterator(this->add_node(np.ptr_, key_hash, pos)); - } - np.ptr_ = node_pointer(); - } - - return result; - } - - //////////////////////////////////////////////////////////////////////// - // Insert range methods - - // if hash function throws, or inserting > 1 element, basic exception - // safety. Strong otherwise - template - void insert_range(I i, I j, - typename boost::unordered::detail::enable_if_forward::type = - 0) - { - if (i == j) - return; - - std::size_t distance = static_cast(std::distance(i, j)); - if (distance == 1) { - emplace_impl(boost::unordered::detail::func::construct_node( - this->node_alloc(), *i)); - } else { - // Only require basic exception safety here - this->reserve_for_insert(this->size_ + distance); - - for (; i != j; ++i) { - emplace_impl_no_rehash( - boost::unordered::detail::func::construct_node( - this->node_alloc(), *i)); - } - } - } - - template - void insert_range(I i, I j, - typename boost::unordered::detail::disable_if_forward::type = - 0) - { - for (; i != j; ++i) { - emplace_impl(boost::unordered::detail::func::construct_node( - this->node_alloc(), *i)); - } - } - - //////////////////////////////////////////////////////////////////////// - // Extract - - inline node_pointer extract_by_iterator(c_iterator n) - { - node_pointer i = n.node_; - BOOST_ASSERT(i); - node_pointer j(node_algo::next_node(i)); - std::size_t bucket_index = this->hash_to_bucket(i->hash_); - // Split the groups containing 'i' and 'j'. - // And get the pointer to the node before i while - // we're at it. - link_pointer prev = node_algo::split_groups(i, j); - - // If we don't have a 'prev' it means that i is at the - // beginning of a block, so search through the blocks in the - // same bucket. - if (!prev) { - prev = this->get_previous_start(bucket_index); - while (prev->next_ != i) { - prev = node_algo::next_for_erase(prev); - } - } - - prev->next_ = i->next_; - --this->size_; - this->fix_bucket(bucket_index, prev); - i->next_ = link_pointer(); - - return i; - } - - //////////////////////////////////////////////////////////////////////// - // Erase - // - // no throw - - std::size_t erase_key(const_key_type& k) - { - if (!this->size_) - return 0; - - std::size_t key_hash = this->hash(k); - std::size_t bucket_index = this->hash_to_bucket(key_hash); - link_pointer prev = this->find_previous_node(k, key_hash, bucket_index); - if (!prev) - return 0; - - node_pointer first_node = node_algo::next_node(prev); - link_pointer end = node_algo::next_group(first_node, this); - - std::size_t deleted_count = this->delete_nodes(prev, end); - this->fix_bucket(bucket_index, prev); - return deleted_count; - } - - iterator erase(c_iterator r) - { - BOOST_ASSERT(r.node_); - node_pointer next = node_algo::next_node(r.node_); - erase_nodes(r.node_, next); - return iterator(next); - } - - iterator erase_range(c_iterator r1, c_iterator r2) - { - if (r1 == r2) - return iterator(r2.node_); - erase_nodes(r1.node_, r2.node_); - return iterator(r2.node_); - } - - link_pointer erase_nodes(node_pointer i, node_pointer j) - { - std::size_t bucket_index = this->hash_to_bucket(i->hash_); - - // Split the groups containing 'i' and 'j'. - // And get the pointer to the node before i while - // we're at it. - link_pointer prev = node_algo::split_groups(i, j); - - // If we don't have a 'prev' it means that i is at the - // beginning of a block, so search through the blocks in the - // same bucket. - if (!prev) { - prev = this->get_previous_start(bucket_index); - while (prev->next_ != i) { - prev = node_algo::next_for_erase(prev); - } - } - - // Delete the nodes. - // Is it inefficient to call fix_bucket for every node? - do { - this->delete_node(prev); - bucket_index = this->fix_bucket(bucket_index, prev); - } while (prev->next_ != j); - - return prev; - } - - //////////////////////////////////////////////////////////////////////// - // fill_buckets - - void copy_buckets(table const& src) - { - this->create_buckets(this->bucket_count_); - - for (node_pointer n = src.begin(); n;) { - std::size_t key_hash = n->hash_; - node_pointer group_end(node_algo::next_group(n, this)); - node_pointer pos = - this->add_node(boost::unordered::detail::func::construct_node( - this->node_alloc(), n->value()), - key_hash, node_pointer()); - for (n = node_algo::next_node(n); n != group_end; - n = node_algo::next_node(n)) { - this->add_node(boost::unordered::detail::func::construct_node( - this->node_alloc(), n->value()), - key_hash, pos); - } - } - } - - void move_buckets(table const& src) - { - this->create_buckets(this->bucket_count_); - - for (node_pointer n = src.begin(); n;) { - std::size_t key_hash = n->hash_; - node_pointer group_end(node_algo::next_group(n, this)); - node_pointer pos = - this->add_node(boost::unordered::detail::func::construct_node( - this->node_alloc(), boost::move(n->value())), - key_hash, node_pointer()); - for (n = node_algo::next_node(n); n != group_end; - n = node_algo::next_node(n)) { - this->add_node(boost::unordered::detail::func::construct_node( - this->node_alloc(), boost::move(n->value())), - key_hash, pos); - } - } - } - - void assign_buckets(table const& src) - { - node_holder holder(*this); - for (node_pointer n = src.begin(); n;) { - std::size_t key_hash = n->hash_; - node_pointer group_end(node_algo::next_group(n, this)); - node_pointer pos = this->add_node( - holder.copy_of(n->value()), key_hash, node_pointer()); - for (n = node_algo::next_node(n); n != group_end; - n = node_algo::next_node(n)) { - this->add_node(holder.copy_of(n->value()), key_hash, pos); - } - } - } - - void move_assign_buckets(table& src) - { - node_holder holder(*this); - for (node_pointer n = src.begin(); n;) { - std::size_t key_hash = n->hash_; - node_pointer group_end(node_algo::next_group(n, this)); - node_pointer pos = this->add_node( - holder.move_copy_of(n->value()), key_hash, node_pointer()); - for (n = node_algo::next_node(n); n != group_end; - n = node_algo::next_node(n)) { - this->add_node(holder.move_copy_of(n->value()), key_hash, pos); - } - } - } }; } } } +#undef BOOST_UNORDERED_EMPLACE_TEMPLATE +#undef BOOST_UNORDERED_EMPLACE_ARGS +#undef BOOST_UNORDERED_EMPLACE_FORWARD +#undef BOOST_UNORDERED_CALL_CONSTRUCT1 +#undef BOOST_UNORDERED_CALL_DESTROY + #endif diff --git a/include/boost/unordered/detail/map.hpp b/include/boost/unordered/detail/map.hpp index cb9a954b..172b4f4b 100644 --- a/include/boost/unordered/detail/map.hpp +++ b/include/boost/unordered/detail/map.hpp @@ -27,65 +27,22 @@ template struct map typedef typename pick::node node; typedef typename pick::bucket bucket; typedef typename pick::link_pointer link_pointer; - typedef typename pick::node_algo node_algo; - typedef boost::unordered::detail::table_impl table; + typedef boost::unordered::detail::table table; typedef boost::unordered::detail::map_extractor extractor; typedef typename boost::unordered::detail::pick_policy::type policy; typedef boost::unordered::iterator_detail::iterator iterator; typedef boost::unordered::iterator_detail::c_iterator c_iterator; - typedef boost::unordered::iterator_detail::l_iterator - l_iterator; - typedef boost::unordered::iterator_detail::cl_iterator - cl_iterator; + typedef boost::unordered::iterator_detail::l_iterator l_iterator; + typedef boost::unordered::iterator_detail::cl_iterator cl_iterator; typedef boost::unordered::node_handle_map node_type; typedef boost::unordered::insert_return_type_map insert_return_type; }; -template -struct multimap -{ - typedef boost::unordered::detail::multimap types; - - typedef std::pair value_type; - typedef H hasher; - typedef P key_equal; - typedef K const const_key_type; - - typedef typename ::boost::unordered::detail::rebind_wrap::type value_allocator; - typedef boost::unordered::detail::allocator_traits - value_allocator_traits; - -#if BOOST_UNORDERED_INTEROPERABLE_NODES - typedef boost::unordered::detail::pick_node pick; -#else - typedef boost::unordered::detail::pick_grouped_node pick; -#endif - typedef typename pick::node node; - typedef typename pick::bucket bucket; - typedef typename pick::link_pointer link_pointer; - typedef typename pick::node_algo node_algo; - - typedef boost::unordered::detail::grouped_table_impl table; - typedef boost::unordered::detail::map_extractor extractor; - - typedef typename boost::unordered::detail::pick_policy::type policy; - - typedef boost::unordered::iterator_detail::iterator iterator; - typedef boost::unordered::iterator_detail::c_iterator c_iterator; - typedef boost::unordered::iterator_detail::l_iterator - l_iterator; - typedef boost::unordered::iterator_detail::cl_iterator - cl_iterator; - - typedef boost::unordered::node_handle_map node_type; -}; - template class instantiate_map { diff --git a/include/boost/unordered/detail/set.hpp b/include/boost/unordered/detail/set.hpp index 42e9cad7..a1649917 100644 --- a/include/boost/unordered/detail/set.hpp +++ b/include/boost/unordered/detail/set.hpp @@ -27,64 +27,22 @@ template struct set typedef typename pick::node node; typedef typename pick::bucket bucket; typedef typename pick::link_pointer link_pointer; - typedef typename pick::node_algo node_algo; - typedef boost::unordered::detail::table_impl table; + typedef boost::unordered::detail::table table; typedef boost::unordered::detail::set_extractor extractor; typedef typename boost::unordered::detail::pick_policy::type policy; typedef boost::unordered::iterator_detail::c_iterator iterator; typedef boost::unordered::iterator_detail::c_iterator c_iterator; - typedef boost::unordered::iterator_detail::cl_iterator - l_iterator; - typedef boost::unordered::iterator_detail::cl_iterator - cl_iterator; + typedef boost::unordered::iterator_detail::cl_iterator l_iterator; + typedef boost::unordered::iterator_detail::cl_iterator cl_iterator; typedef boost::unordered::node_handle_set node_type; typedef boost::unordered::insert_return_type_set insert_return_type; }; -template struct multiset -{ - typedef boost::unordered::detail::multiset types; - - typedef T value_type; - typedef H hasher; - typedef P key_equal; - typedef T const const_key_type; - - typedef typename ::boost::unordered::detail::rebind_wrap::type value_allocator; - typedef boost::unordered::detail::allocator_traits - value_allocator_traits; - -#if BOOST_UNORDERED_INTEROPERABLE_NODES - typedef boost::unordered::detail::pick_node pick; -#else - typedef boost::unordered::detail::pick_grouped_node pick; -#endif - typedef typename pick::node node; - typedef typename pick::bucket bucket; - typedef typename pick::link_pointer link_pointer; - typedef typename pick::node_algo node_algo; - - typedef boost::unordered::detail::grouped_table_impl table; - typedef boost::unordered::detail::set_extractor extractor; - - typedef typename boost::unordered::detail::pick_policy::type policy; - - typedef boost::unordered::iterator_detail::c_iterator iterator; - typedef boost::unordered::iterator_detail::c_iterator c_iterator; - typedef boost::unordered::iterator_detail::cl_iterator - l_iterator; - typedef boost::unordered::iterator_detail::cl_iterator - cl_iterator; - - typedef boost::unordered::node_handle_set node_type; -}; - template class instantiate_set { typedef boost::unordered_set container; diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 64e5b1fd..a0b4d532 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) @@ -44,8 +45,8 @@ template class unordered_map public: typedef K key_type; - typedef std::pair value_type; typedef T mapped_type; + typedef std::pair value_type; typedef H hasher; typedef P key_equal; typedef A allocator_type; @@ -54,6 +55,8 @@ template class unordered_map typedef boost::unordered::detail::map types; typedef typename types::value_allocator_traits value_allocator_traits; typedef typename types::table table; + typedef typename table::node_pointer node_pointer; + typedef typename table::link_pointer link_pointer; public: typedef typename value_allocator_traits::pointer pointer; @@ -65,10 +68,10 @@ template class unordered_map typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; - typedef typename table::cl_iterator const_local_iterator; - typedef typename table::l_iterator local_iterator; - typedef typename table::c_iterator const_iterator; typedef typename table::iterator iterator; + typedef typename table::c_iterator const_iterator; + typedef typename table::l_iterator local_iterator; + typedef typename table::cl_iterator const_local_iterator; typedef typename types::node_type node_type; typedef typename types::insert_return_type insert_return_type; @@ -84,59 +87,54 @@ template class unordered_map const key_equal& = key_equal(), const allocator_type& = allocator_type()); - explicit unordered_map(size_type, const allocator_type&); - - explicit unordered_map(size_type, const hasher&, const allocator_type&); - - explicit unordered_map(allocator_type const&); - - template unordered_map(InputIt, InputIt); - template - unordered_map(InputIt, InputIt, size_type, const hasher& = hasher(), - const key_equal& = key_equal()); - - template - unordered_map(InputIt, InputIt, size_type, const hasher&, const key_equal&, - const allocator_type&); - - template - unordered_map( - InputIt, InputIt, size_type, const hasher&, const allocator_type&); - - template - unordered_map(InputIt, InputIt, size_type, const allocator_type&); - - // copy/move constructors + unordered_map(InputIt, InputIt, + size_type = boost::unordered::detail::default_bucket_count, + const hasher& = hasher(), const key_equal& = key_equal(), + const allocator_type& = allocator_type()); unordered_map(unordered_map const&); - unordered_map(unordered_map const&, allocator_type const&); - unordered_map(BOOST_RV_REF(unordered_map), allocator_type const&); - -#if defined(BOOST_UNORDERED_USE_MOVE) +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) unordered_map(BOOST_RV_REF(unordered_map) other) BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) : table_(other.table_, boost::unordered::detail::move_tag()) { - } -#elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) - unordered_map(unordered_map&& other) - BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) - : table_(other.table_, boost::unordered::detail::move_tag()) - { + // The move is done in table_ } #endif + explicit unordered_map(allocator_type const&); + + unordered_map(unordered_map const&, allocator_type const&); + + unordered_map(BOOST_RV_REF(unordered_map), allocator_type const&); + #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) unordered_map(std::initializer_list, size_type = boost::unordered::detail::default_bucket_count, const hasher& = hasher(), const key_equal& l = key_equal(), const allocator_type& = allocator_type()); - unordered_map(std::initializer_list, size_type, const hasher&, - const allocator_type&); +#endif + + explicit unordered_map(size_type, const allocator_type&); + + explicit unordered_map(size_type, const hasher&, const allocator_type&); + + template + unordered_map(InputIt, InputIt, size_type, const allocator_type&); + + template + unordered_map( + InputIt, InputIt, size_type, const hasher&, const allocator_type&); + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) unordered_map( std::initializer_list, size_type, const allocator_type&); + + unordered_map(std::initializer_list, size_type, const hasher&, + const allocator_type&); #endif // Destructor @@ -148,26 +146,34 @@ template class unordered_map #if defined(BOOST_UNORDERED_USE_MOVE) unordered_map& operator=(BOOST_COPY_ASSIGN_REF(unordered_map) x) { - table_.assign(x.table_); + table_.assign(x.table_, boost::unordered::detail::true_type()); return *this; } unordered_map& operator=(BOOST_RV_REF(unordered_map) x) + // C++17 support: BOOST_NOEXCEPT_IF( + // value_allocator_traits::is_always_equal::value && + // is_nothrow_move_assignable_v && + // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, boost::unordered::detail::true_type()); return *this; } #else unordered_map& operator=(unordered_map const& x) { - table_.assign(x.table_); + table_.assign(x.table_, boost::unordered::detail::true_type()); return *this; } #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) unordered_map& operator=(unordered_map&& x) + // C++17 support: BOOST_NOEXCEPT_IF( + // value_allocator_traits::is_always_equal::value && + // is_nothrow_move_assignable_v && + // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, boost::unordered::detail::true_type()); return *this; } #endif @@ -182,14 +188,6 @@ template class unordered_map return table_.node_alloc(); } - // size and capacity - - bool empty() const BOOST_NOEXCEPT { return table_.size_ == 0; } - - size_type size() const BOOST_NOEXCEPT { return table_.size_; } - - size_type max_size() const BOOST_NOEXCEPT; - // iterators iterator begin() BOOST_NOEXCEPT { return iterator(table_.begin()); } @@ -210,67 +208,29 @@ template class unordered_map const_iterator cend() const BOOST_NOEXCEPT { return const_iterator(); } - // extract + // size and capacity - node_type extract(const_iterator position) - { - return node_type( - table_.extract_by_iterator(position), table_.node_alloc()); - } + bool empty() const BOOST_NOEXCEPT { return table_.size_ == 0; } - node_type extract(const key_type& k) - { - return node_type(table_.extract_by_key(k), table_.node_alloc()); - } + size_type size() const BOOST_NOEXCEPT { return table_.size_; } + + size_type max_size() const BOOST_NOEXCEPT; // emplace #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + template std::pair emplace(BOOST_FWD_REF(Args)... args) { - return table_.emplace(boost::forward(args)...); + return table_.emplace_unique( + table::extractor::extract(boost::forward(args)...), + boost::forward(args)...); } - template - iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args) - { - return table_.emplace_hint(hint, boost::forward(args)...); - } - - template - std::pair try_emplace( - key_type const& k, BOOST_FWD_REF(Args)... args) - { - return table_.try_emplace_impl(k, boost::forward(args)...); - } - - template - iterator try_emplace( - const_iterator hint, key_type const& k, BOOST_FWD_REF(Args)... args) - { - return table_.try_emplace_hint_impl( - hint, k, boost::forward(args)...); - } - - template - std::pair try_emplace( - BOOST_RV_REF(key_type) k, BOOST_FWD_REF(Args)... args) - { - return table_.try_emplace_impl( - boost::move(k), boost::forward(args)...); - } - - template - iterator try_emplace(const_iterator hint, BOOST_RV_REF(key_type) k, - BOOST_FWD_REF(Args)... args) - { - return table_.try_emplace_hint_impl( - hint, boost::move(k), boost::forward(args)...); - } #else -#if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100)) +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 // 0 argument emplace requires special treatment in case // the container is instantiated with a value type that @@ -284,6 +244,56 @@ template class unordered_map return this->emplace(boost::move(v)); } +#endif + + template + std::pair emplace(BOOST_FWD_REF(A0) a0) + { + return table_.emplace_unique( + table::extractor::extract(boost::forward(a0)), + boost::unordered::detail::create_emplace_args( + boost::forward(a0))); + } + + template + std::pair emplace( + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return table_.emplace_unique( + table::extractor::extract( + boost::forward(a0), boost::forward(a1)), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))); + } + + template + std::pair emplace( + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return table_.emplace_unique( + table::extractor::extract( + boost::forward(a0), boost::forward(a1)), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))); + } + +#endif + +#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + template + iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args) + { + return table_.emplace_hint_unique(hint, + table::extractor::extract(boost::forward(args)...), + boost::forward(args)...); + } + +#else + +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 + iterator emplace_hint(const_iterator hint, boost::unordered::detail::empty_emplace = boost::unordered::detail::empty_emplace(), @@ -294,242 +304,73 @@ template class unordered_map #endif - template - std::pair try_emplace(BOOST_FWD_REF(Key) k) - { - return table_.try_emplace_impl(boost::forward(k)); - } - - template - iterator try_emplace(const_iterator hint, BOOST_FWD_REF(Key) k) - { - return table_.try_emplace_hint_impl(hint, boost::forward(k)); - } - - template - std::pair emplace(BOOST_FWD_REF(A0) a0) - { - return table_.emplace(boost::unordered::detail::create_emplace_args( - boost::forward(a0))); - } - template iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(A0) a0) { - return table_.emplace_hint( - hint, boost::unordered::detail::create_emplace_args( - boost::forward(a0))); - } - - template - std::pair try_emplace( - key_type const& k, BOOST_FWD_REF(A0) a0) - { - return table_.try_emplace_impl( - k, boost::unordered::detail::create_emplace_args( - boost::forward(a0))); - } - - template - iterator try_emplace( - const_iterator hint, key_type const& k, BOOST_FWD_REF(A0) a0) - { - return table_.try_emplace_hint_impl( - hint, k, boost::unordered::detail::create_emplace_args( - boost::forward(a0))); - } - - template - std::pair try_emplace( - BOOST_RV_REF(key_type) k, BOOST_FWD_REF(A0) a0) - { - return table_.try_emplace_impl( - boost::move(k), boost::unordered::detail::create_emplace_args( - boost::forward(a0))); - } - - template - iterator try_emplace( - const_iterator hint, BOOST_RV_REF(key_type) k, BOOST_FWD_REF(A0) a0) - { - return table_.try_emplace_hint_impl( - hint, boost::move(k), boost::unordered::detail::create_emplace_args( - boost::forward(a0))); - } - - template - std::pair emplace( - BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) - { - return table_.emplace(boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1))); + return table_.emplace_hint_unique(hint, + table::extractor::extract(boost::forward(a0)), + boost::unordered::detail::create_emplace_args( + boost::forward(a0))); } template iterator emplace_hint( const_iterator hint, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) { - return table_.emplace_hint( - hint, boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1))); - } - - template - std::pair try_emplace( - key_type const& k, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) - { - return table_.try_emplace_impl( - k, boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1))); - } - - template - iterator try_emplace(const_iterator hint, key_type const& k, - BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) - { - return table_.try_emplace_hint_impl( - hint, k, boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1))); - } - - template - std::pair try_emplace( - BOOST_RV_REF(key_type) k, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) - { - return table_.try_emplace_impl( - boost::move(k), + return table_.emplace_hint_unique( + hint, table::extractor::extract( + boost::forward(a0), boost::forward(a1)), boost::unordered::detail::create_emplace_args( boost::forward(a0), boost::forward(a1))); } - template - iterator try_emplace(const_iterator hint, BOOST_RV_REF(key_type) k, - BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) - { - return table_.try_emplace_hint_impl( - hint, boost::move(k), - boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1))); - } - - template - std::pair emplace( - BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) - { - return table_.emplace(boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1), - boost::forward(a2))); - } - template iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) { - return table_.emplace_hint( - hint, boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1), - boost::forward(a2))); - } - - template - std::pair try_emplace(key_type const& k, - BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) - { - return table_.try_emplace_impl( - k, boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1), - boost::forward(a2))); - } - - template - iterator try_emplace(const_iterator hint, key_type const& k, - BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) - { - return table_ - .try_emplace_impl_( - hint, k, boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1), - boost::forward(a2))) - .first; - } - - template - std::pair try_emplace(BOOST_RV_REF(key_type) k, - BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) - { - return table_.try_emplace_impl( - boost::move(k), boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1), - boost::forward(a2))); - } - - template - iterator try_emplace(const_iterator hint, BOOST_RV_REF(key_type) k, - BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) - { - return table_.try_emplace_hint_impl( - hint, boost::move(k), + return table_.emplace_hint_unique( + hint, table::extractor::extract( + boost::forward(a0), boost::forward(a1)), boost::unordered::detail::create_emplace_args( boost::forward(a0), boost::forward(a1), boost::forward(a2))); } +#endif + +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + #define BOOST_UNORDERED_EMPLACE(z, n, _) \ template \ std::pair emplace( \ BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ { \ - return table_.emplace(boost::unordered::detail::create_emplace_args( \ - BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + return table_.emplace_unique( \ + table::extractor::extract( \ + boost::forward(a0), boost::forward(a1)), \ + boost::unordered::detail::create_emplace_args( \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ } \ \ template \ iterator emplace_hint(const_iterator hint, \ BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ { \ - return table_.emplace_hint( \ - hint, boost::unordered::detail::create_emplace_args( \ - BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ - } \ - \ - template \ - std::pair try_emplace( \ - key_type const& k, BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ - { \ - return table_.try_emplace_impl( \ - k, boost::unordered::detail::create_emplace_args( \ - BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ - } \ - \ - template \ - iterator try_emplace(const_iterator hint, key_type const& k, \ - BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ - { \ - return table_.try_emplace_hint_impl(hint, k, \ - boost::unordered::detail::create_emplace_args(BOOST_PP_ENUM_##z( \ - n, BOOST_UNORDERED_CALL_FORWARD, a))); \ - } \ - \ - template \ - std::pair try_emplace(BOOST_RV_REF(key_type) k, \ - BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ - { \ - return table_.try_emplace_impl(boost::move(k), \ - boost::unordered::detail::create_emplace_args(BOOST_PP_ENUM_##z( \ - n, BOOST_UNORDERED_CALL_FORWARD, a))); \ - } \ - \ - template \ - iterator try_emplace(const_iterator hint, BOOST_RV_REF(key_type) k, \ - BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ - { \ - return table_.try_emplace_hint_impl(hint, boost::move(k), \ - boost::unordered::detail::create_emplace_args(BOOST_PP_ENUM_##z( \ - n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + return table_.emplace_hint_unique( \ + hint, table::extractor::extract( \ + boost::forward(a0), boost::forward(a1)), \ + boost::unordered::detail::create_emplace_args( \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ } - BOOST_PP_REPEAT_FROM_TO( - 4, BOOST_UNORDERED_EMPLACE_LIMIT, BOOST_UNORDERED_EMPLACE, _) + BOOST_UNORDERED_EMPLACE(1, 4, _) + BOOST_UNORDERED_EMPLACE(1, 5, _) + BOOST_UNORDERED_EMPLACE(1, 6, _) + BOOST_UNORDERED_EMPLACE(1, 7, _) + BOOST_UNORDERED_EMPLACE(1, 8, _) + BOOST_UNORDERED_EMPLACE(1, 9, _) + BOOST_PP_REPEAT_FROM_TO(10, BOOST_PP_INC(BOOST_UNORDERED_EMPLACE_LIMIT), + BOOST_UNORDERED_EMPLACE, _) #undef BOOST_UNORDERED_EMPLACE @@ -545,6 +386,15 @@ template class unordered_map return this->emplace(boost::move(x)); } + template + std::pair insert(BOOST_RV_REF(P2) obj, + typename boost::enable_if_c< + boost::is_constructible::value, + void*>::type = 0) + { + return this->emplace(boost::forward(obj)); + } + iterator insert(const_iterator hint, value_type const& x) { return this->emplace_hint(hint, x); @@ -555,35 +405,13 @@ template class unordered_map return this->emplace_hint(hint, boost::move(x)); } - template - std::pair insert_or_assign( - key_type const& k, BOOST_FWD_REF(M) obj) + template + iterator insert(const_iterator hint, BOOST_RV_REF(P2) obj, + typename boost::enable_if_c< + boost::is_constructible::value, + void*>::type = 0) { - return table_.insert_or_assign_impl(k, boost::forward(obj)); - } - - template - iterator insert_or_assign( - const_iterator, key_type const& k, BOOST_FWD_REF(M) obj) - { - return table_.insert_or_assign_impl(k, boost::forward(obj)).first; - } - - template - std::pair insert_or_assign( - BOOST_RV_REF(key_type) k, BOOST_FWD_REF(M) obj) - { - return table_.insert_or_assign_impl( - boost::move(k), boost::forward(obj)); - } - - template - iterator insert_or_assign( - const_iterator, BOOST_RV_REF(key_type) k, BOOST_FWD_REF(M) obj) - { - return table_ - .insert_or_assign_impl(boost::move(k), boost::forward(obj)) - .first; + return this->emplace_hint(hint, boost::forward(obj)); } template void insert(InputIt, InputIt); @@ -592,19 +420,33 @@ template class unordered_map void insert(std::initializer_list); #endif + // extract + + node_type extract(const_iterator position) + { + return node_type( + table_.extract_by_iterator_unique(position), table_.node_alloc()); + } + + node_type extract(const key_type& k) + { + return node_type(table_.extract_by_key(k), table_.node_alloc()); + } + insert_return_type insert(BOOST_RV_REF(node_type) np) { insert_return_type result; - table_.move_insert_node_type(np, result); + table_.move_insert_node_type_unique(np, result); return boost::move(result); } iterator insert(const_iterator hint, BOOST_RV_REF(node_type) np) { - return table_.move_insert_node_type_with_hint(hint, np); + return table_.move_insert_node_type_with_hint_unique(hint, np); } -#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || \ + (BOOST_COMP_GNUC && BOOST_COMP_GNUC < BOOST_VERSION_NUMBER(4, 6, 0)) private: // Note: Use r-value node_type to insert. insert_return_type insert(node_type&); @@ -613,14 +455,281 @@ template class unordered_map public: #endif +#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + template + std::pair try_emplace( + key_type const& k, BOOST_FWD_REF(Args)... args) + { + return table_.try_emplace_unique(k, boost::forward(args)...); + } + + template + std::pair try_emplace( + BOOST_RV_REF(key_type) k, BOOST_FWD_REF(Args)... args) + { + return table_.try_emplace_unique( + boost::move(k), boost::forward(args)...); + } + + template + iterator try_emplace( + const_iterator hint, key_type const& k, BOOST_FWD_REF(Args)... args) + { + return table_.try_emplace_hint_unique( + hint, k, boost::forward(args)...); + } + + template + iterator try_emplace(const_iterator hint, BOOST_RV_REF(key_type) k, + BOOST_FWD_REF(Args)... args) + { + return table_.try_emplace_hint_unique( + hint, boost::move(k), boost::forward(args)...); + } + +#else + + // In order to make this a template, this handles both: + // try_emplace(key const&) + // try_emplace(key&&) + + template + std::pair try_emplace(BOOST_FWD_REF(Key) k) + { + return table_.try_emplace_unique(boost::forward(k)); + } + + // In order to make this a template, this handles both: + // try_emplace(const_iterator hint, key const&) + // try_emplace(const_iterator hint, key&&) + + template + iterator try_emplace(const_iterator hint, BOOST_FWD_REF(Key) k) + { + return table_.try_emplace_hint_unique(hint, boost::forward(k)); + } + + // try_emplace(key const&, Args&&...) + + template + std::pair try_emplace( + key_type const& k, BOOST_FWD_REF(A0) a0) + { + return table_.try_emplace_unique( + k, boost::unordered::detail::create_emplace_args( + boost::forward(a0))); + } + + template + std::pair try_emplace( + key_type const& k, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return table_.try_emplace_unique( + k, boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))); + } + + template + std::pair try_emplace(key_type const& k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return table_.try_emplace_unique( + k, boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))); + } + + // try_emplace(key&&, Args&&...) + + template + std::pair try_emplace( + BOOST_RV_REF(key_type) k, BOOST_FWD_REF(A0) a0) + { + return table_.try_emplace_unique( + boost::move(k), boost::unordered::detail::create_emplace_args( + boost::forward(a0))); + } + + template + std::pair try_emplace( + BOOST_RV_REF(key_type) k, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return table_.try_emplace_unique( + boost::move(k), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))); + } + + template + std::pair try_emplace(BOOST_RV_REF(key_type) k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return table_.try_emplace_unique( + boost::move(k), boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))); + } + + // try_emplace(const_iterator hint, key const&, Args&&...) + + template + iterator try_emplace( + const_iterator hint, key_type const& k, BOOST_FWD_REF(A0) a0) + { + return table_.try_emplace_hint_unique( + hint, k, boost::unordered::detail::create_emplace_args( + boost::forward(a0))); + } + + template + iterator try_emplace(const_iterator hint, key_type const& k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return table_.try_emplace_hint_unique( + hint, k, boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))); + } + + template + iterator try_emplace(const_iterator hint, key_type const& k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return table_.try_emplace_hint_unique( + hint, k, boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))); + } + + // try_emplace(const_iterator hint, key&&, Args&&...) + + template + iterator try_emplace( + const_iterator hint, BOOST_RV_REF(key_type) k, BOOST_FWD_REF(A0) a0) + { + return table_.try_emplace_hint_unique( + hint, boost::move(k), boost::unordered::detail::create_emplace_args( + boost::forward(a0))); + } + + template + iterator try_emplace(const_iterator hint, BOOST_RV_REF(key_type) k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return table_.try_emplace_hint_unique( + hint, boost::move(k), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))); + } + + template + iterator try_emplace(const_iterator hint, BOOST_RV_REF(key_type) k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return table_.try_emplace_hint_unique( + hint, boost::move(k), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))); + } + +#define BOOST_UNORDERED_TRY_EMPLACE(z, n, _) \ + \ + template \ + std::pair try_emplace( \ + key_type const& k, BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ + { \ + return table_.try_emplace_unique( \ + k, boost::unordered::detail::create_emplace_args( \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + } \ + \ + template \ + std::pair try_emplace(BOOST_RV_REF(key_type) k, \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ + { \ + return table_.try_emplace_unique(boost::move(k), \ + boost::unordered::detail::create_emplace_args(BOOST_PP_ENUM_##z( \ + n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + } \ + \ + template \ + iterator try_emplace(const_iterator hint, key_type const& k, \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ + { \ + return table_.try_emplace_hint_unique(hint, k, \ + boost::unordered::detail::create_emplace_args(BOOST_PP_ENUM_##z( \ + n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + } \ + \ + template \ + iterator try_emplace(const_iterator hint, BOOST_RV_REF(key_type) k, \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ + { \ + return table_.try_emplace_hint_unique(hint, boost::move(k), \ + boost::unordered::detail::create_emplace_args(BOOST_PP_ENUM_##z( \ + n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + } + + BOOST_UNORDERED_TRY_EMPLACE(1, 4, _) + BOOST_UNORDERED_TRY_EMPLACE(1, 5, _) + BOOST_UNORDERED_TRY_EMPLACE(1, 6, _) + BOOST_UNORDERED_TRY_EMPLACE(1, 7, _) + BOOST_UNORDERED_TRY_EMPLACE(1, 8, _) + BOOST_UNORDERED_TRY_EMPLACE(1, 9, _) + BOOST_PP_REPEAT_FROM_TO(10, BOOST_PP_INC(BOOST_UNORDERED_EMPLACE_LIMIT), + BOOST_UNORDERED_TRY_EMPLACE, _) + +#undef BOOST_UNORDERED_TRY_EMPLACE + +#endif + + template + std::pair insert_or_assign( + key_type const& k, BOOST_FWD_REF(M) obj) + { + return table_.insert_or_assign_unique(k, boost::forward(obj)); + } + + template + std::pair insert_or_assign( + BOOST_RV_REF(key_type) k, BOOST_FWD_REF(M) obj) + { + return table_.insert_or_assign_unique( + boost::move(k), boost::forward(obj)); + } + + template + iterator insert_or_assign( + const_iterator, key_type const& k, BOOST_FWD_REF(M) obj) + { + return table_.insert_or_assign_unique(k, boost::forward(obj)).first; + } + + template + iterator insert_or_assign( + const_iterator, BOOST_RV_REF(key_type) k, BOOST_FWD_REF(M) obj) + { + return table_ + .insert_or_assign_unique(boost::move(k), boost::forward(obj)) + .first; + } + + iterator erase(iterator); iterator erase(const_iterator); size_type erase(const key_type&); iterator erase(const_iterator, const_iterator); + BOOST_UNORDERED_DEPRECATED("Use erase instead") void quick_erase(const_iterator it) { erase(it); } + BOOST_UNORDERED_DEPRECATED("Use erase instead") void erase_return_void(const_iterator it) { erase(it); } - void clear(); void swap(unordered_map&); + // C++17 support: BOOST_NOEXCEPT_IF( + // value_allocator_traits::is_always_equal::value && + // is_nothrow_move_assignable_v && + // is_nothrow_move_assignable_v

) + void clear() BOOST_NOEXCEPT { table_.clear_impl(); } template void merge(boost::unordered_map& source); @@ -630,14 +739,12 @@ template class unordered_map void merge(boost::unordered_map&& source); #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template void merge(boost::unordered_multimap& source); #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) template void merge(boost::unordered_multimap&& source); -#endif #endif // observers @@ -645,10 +752,6 @@ template class unordered_map hasher hash_function() const; key_equal key_eq() const; - mapped_type& operator[](const key_type&); - mapped_type& at(const key_type&); - mapped_type const& at(const key_type&) const; - // lookup iterator find(const key_type&); @@ -670,6 +773,11 @@ template class unordered_map std::pair equal_range( const key_type&) const; + mapped_type& operator[](const key_type&); + mapped_type& operator[](BOOST_RV_REF(key_type)); + mapped_type& at(const key_type&); + mapped_type const& at(const key_type&) const; + // bucket interface size_type bucket_count() const BOOST_NOEXCEPT @@ -715,9 +823,8 @@ template class unordered_map // hash policy - float max_load_factor() const BOOST_NOEXCEPT { return table_.mlf_; } - float load_factor() const BOOST_NOEXCEPT; + float max_load_factor() const BOOST_NOEXCEPT { return table_.mlf_; } void max_load_factor(float) BOOST_NOEXCEPT; void rehash(size_type); void reserve(size_type); @@ -740,16 +847,18 @@ template class unordered_multimap public: typedef K key_type; - typedef std::pair value_type; typedef T mapped_type; + typedef std::pair value_type; typedef H hasher; typedef P key_equal; typedef A allocator_type; private: - typedef boost::unordered::detail::multimap types; + typedef boost::unordered::detail::map types; typedef typename types::value_allocator_traits value_allocator_traits; typedef typename types::table table; + typedef typename table::node_pointer node_pointer; + typedef typename table::link_pointer link_pointer; public: typedef typename value_allocator_traits::pointer pointer; @@ -761,10 +870,10 @@ template class unordered_multimap typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; - typedef typename table::cl_iterator const_local_iterator; - typedef typename table::l_iterator local_iterator; - typedef typename table::c_iterator const_iterator; typedef typename table::iterator iterator; + typedef typename table::c_iterator const_iterator; + typedef typename table::l_iterator local_iterator; + typedef typename table::cl_iterator const_local_iterator; typedef typename types::node_type node_type; private: @@ -779,60 +888,55 @@ template class unordered_multimap const key_equal& = key_equal(), const allocator_type& = allocator_type()); - explicit unordered_multimap(size_type, const allocator_type&); - - explicit unordered_multimap( - size_type, const hasher&, const allocator_type&); - - explicit unordered_multimap(allocator_type const&); - - template unordered_multimap(InputIt, InputIt); - template - unordered_multimap(InputIt, InputIt, size_type, const hasher& = hasher(), - const key_equal& = key_equal()); - - template - unordered_multimap(InputIt, InputIt, size_type, const hasher&, - const key_equal&, const allocator_type&); - - template - unordered_multimap( - InputIt, InputIt, size_type, const hasher&, const allocator_type&); - - template - unordered_multimap(InputIt, InputIt, size_type, const allocator_type&); - - // copy/move constructors + unordered_multimap(InputIt, InputIt, + size_type = boost::unordered::detail::default_bucket_count, + const hasher& = hasher(), const key_equal& = key_equal(), + const allocator_type& = allocator_type()); unordered_multimap(unordered_multimap const&); - unordered_multimap(unordered_multimap const&, allocator_type const&); - unordered_multimap(BOOST_RV_REF(unordered_multimap), allocator_type const&); - -#if defined(BOOST_UNORDERED_USE_MOVE) +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) unordered_multimap(BOOST_RV_REF(unordered_multimap) other) BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) : table_(other.table_, boost::unordered::detail::move_tag()) { - } -#elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) - unordered_multimap(unordered_multimap&& other) - BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) - : table_(other.table_, boost::unordered::detail::move_tag()) - { + // The move is done in table_ } #endif + explicit unordered_multimap(allocator_type const&); + + unordered_multimap(unordered_multimap const&, allocator_type const&); + + unordered_multimap(BOOST_RV_REF(unordered_multimap), allocator_type const&); + #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) unordered_multimap(std::initializer_list, size_type = boost::unordered::detail::default_bucket_count, const hasher& = hasher(), const key_equal& l = key_equal(), const allocator_type& = allocator_type()); - unordered_multimap(std::initializer_list, size_type, - const hasher&, const allocator_type&); +#endif + + explicit unordered_multimap(size_type, const allocator_type&); + + explicit unordered_multimap( + size_type, const hasher&, const allocator_type&); + + template + unordered_multimap(InputIt, InputIt, size_type, const allocator_type&); + + template + unordered_multimap( + InputIt, InputIt, size_type, const hasher&, const allocator_type&); + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) unordered_multimap( std::initializer_list, size_type, const allocator_type&); + + unordered_multimap(std::initializer_list, size_type, + const hasher&, const allocator_type&); #endif // Destructor @@ -844,26 +948,34 @@ template class unordered_multimap #if defined(BOOST_UNORDERED_USE_MOVE) unordered_multimap& operator=(BOOST_COPY_ASSIGN_REF(unordered_multimap) x) { - table_.assign(x.table_); + table_.assign(x.table_, boost::unordered::detail::false_type()); return *this; } unordered_multimap& operator=(BOOST_RV_REF(unordered_multimap) x) + // C++17 support: BOOST_NOEXCEPT_IF( + // value_allocator_traits::is_always_equal::value && + // is_nothrow_move_assignable_v && + // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, boost::unordered::detail::false_type()); return *this; } #else unordered_multimap& operator=(unordered_multimap const& x) { - table_.assign(x.table_); + table_.assign(x.table_, boost::unordered::detail::false_type()); return *this; } #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) unordered_multimap& operator=(unordered_multimap&& x) + // C++17 support: BOOST_NOEXCEPT_IF( + // value_allocator_traits::is_always_equal::value && + // is_nothrow_move_assignable_v && + // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, boost::unordered::detail::false_type()); return *this; } #endif @@ -878,14 +990,6 @@ template class unordered_multimap return table_.node_alloc(); } - // size and capacity - - bool empty() const BOOST_NOEXCEPT { return table_.size_ == 0; } - - size_type size() const BOOST_NOEXCEPT { return table_.size_; } - - size_type max_size() const BOOST_NOEXCEPT; - // iterators iterator begin() BOOST_NOEXCEPT { return iterator(table_.begin()); } @@ -906,35 +1010,28 @@ template class unordered_multimap const_iterator cend() const BOOST_NOEXCEPT { return const_iterator(); } - // extract + // size and capacity - node_type extract(const_iterator position) - { - return node_type( - table_.extract_by_iterator(position), table_.node_alloc()); - } + bool empty() const BOOST_NOEXCEPT { return table_.size_ == 0; } - node_type extract(const key_type& k) - { - return node_type(table_.extract_by_key(k), table_.node_alloc()); - } + size_type size() const BOOST_NOEXCEPT { return table_.size_; } + + size_type max_size() const BOOST_NOEXCEPT; // emplace #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + template iterator emplace(BOOST_FWD_REF(Args)... args) { - return table_.emplace(boost::forward(args)...); + return iterator(table_.emplace_equiv( + boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), boost::forward(args)...))); } - template - iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args) - { - return table_.emplace_hint(hint, boost::forward(args)...); - } #else -#if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100)) +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 // 0 argument emplace requires special treatment in case // the container is instantiated with a value type that @@ -947,6 +1044,55 @@ template class unordered_multimap return this->emplace(boost::move(v)); } +#endif + + template iterator emplace(BOOST_FWD_REF(A0) a0) + { + return iterator(table_.emplace_equiv( + boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), + boost::unordered::detail::create_emplace_args( + boost::forward(a0))))); + } + + template + iterator emplace(BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return iterator(table_.emplace_equiv( + boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))))); + } + + template + iterator emplace( + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return iterator(table_.emplace_equiv( + boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))))); + } + +#endif + +#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + template + iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args) + { + return iterator(table_.emplace_hint_equiv( + hint, boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), boost::forward(args)...))); + } + +#else + +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 + iterator emplace_hint(const_iterator hint, boost::unordered::detail::empty_emplace = boost::unordered::detail::empty_emplace(), @@ -957,74 +1103,74 @@ template class unordered_multimap #endif - template iterator emplace(BOOST_FWD_REF(A0) a0) - { - return table_.emplace(boost::unordered::detail::create_emplace_args( - boost::forward(a0))); - } - template iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(A0) a0) { - return table_.emplace_hint( - hint, boost::unordered::detail::create_emplace_args( - boost::forward(a0))); - } - - template - iterator emplace(BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) - { - return table_.emplace(boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1))); + return iterator(table_.emplace_hint_equiv( + hint, boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), + boost::unordered::detail::create_emplace_args( + boost::forward(a0))))); } template iterator emplace_hint( const_iterator hint, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) { - return table_.emplace_hint( - hint, boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1))); - } - - template - iterator emplace( - BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) - { - return table_.emplace(boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1), - boost::forward(a2))); + return iterator(table_.emplace_hint_equiv( + hint, boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))))); } template iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) { - return table_.emplace_hint( - hint, boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1), - boost::forward(a2))); + return iterator(table_.emplace_hint_equiv( + hint, boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))))); } +#endif + +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + #define BOOST_UNORDERED_EMPLACE(z, n, _) \ template \ iterator emplace(BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ { \ - return table_.emplace(boost::unordered::detail::create_emplace_args( \ - BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + return iterator(table_.emplace_equiv( \ + boost::unordered::detail::func::construct_node_from_args( \ + table_.node_alloc(), \ + boost::unordered::detail::create_emplace_args( \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))))); \ } \ \ template \ iterator emplace_hint(const_iterator hint, \ BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ { \ - return table_.emplace_hint( \ - hint, boost::unordered::detail::create_emplace_args( \ - BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + return iterator(table_.emplace_hint_equiv( \ + hint, \ + boost::unordered::detail::func::construct_node_from_args( \ + table_.node_alloc(), \ + boost::unordered::detail::create_emplace_args( \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))))); \ } - BOOST_PP_REPEAT_FROM_TO( - 4, BOOST_UNORDERED_EMPLACE_LIMIT, BOOST_UNORDERED_EMPLACE, _) + BOOST_UNORDERED_EMPLACE(1, 4, _) + BOOST_UNORDERED_EMPLACE(1, 5, _) + BOOST_UNORDERED_EMPLACE(1, 6, _) + BOOST_UNORDERED_EMPLACE(1, 7, _) + BOOST_UNORDERED_EMPLACE(1, 8, _) + BOOST_UNORDERED_EMPLACE(1, 9, _) + BOOST_PP_REPEAT_FROM_TO(10, BOOST_PP_INC(BOOST_UNORDERED_EMPLACE_LIMIT), + BOOST_UNORDERED_EMPLACE, _) #undef BOOST_UNORDERED_EMPLACE @@ -1037,6 +1183,15 @@ template class unordered_multimap return this->emplace(boost::move(x)); } + template + iterator insert(BOOST_RV_REF(P2) obj, + typename boost::enable_if_c< + boost::is_constructible::value, + void*>::type = 0) + { + return this->emplace(boost::forward(obj)); + } + iterator insert(const_iterator hint, value_type const& x) { return this->emplace_hint(hint, x); @@ -1047,23 +1202,46 @@ template class unordered_multimap return this->emplace_hint(hint, boost::move(x)); } + template + iterator insert(const_iterator hint, BOOST_RV_REF(P2) obj, + typename boost::enable_if_c< + boost::is_constructible::value, + void*>::type = 0) + { + return this->emplace_hint(hint, boost::forward(obj)); + } + template void insert(InputIt, InputIt); #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) void insert(std::initializer_list); #endif + // extract + + node_type extract(const_iterator position) + { + return node_type( + table_.extract_by_iterator_equiv(position), table_.node_alloc()); + } + + node_type extract(const key_type& k) + { + return node_type(table_.extract_by_key(k), table_.node_alloc()); + } + iterator insert(BOOST_RV_REF(node_type) np) { - return table_.move_insert_node_type(np); + return table_.move_insert_node_type_equiv(np); } iterator insert(const_iterator hint, BOOST_RV_REF(node_type) np) { - return table_.move_insert_node_type_with_hint(hint, np); + return table_.move_insert_node_type_with_hint_equiv(hint, np); } -#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || \ + (BOOST_COMP_GNUC && BOOST_COMP_GNUC < BOOST_VERSION_NUMBER(4, 6, 0)) private: // Note: Use r-value node_type to insert. iterator insert(node_type&); @@ -1072,14 +1250,21 @@ template class unordered_multimap public: #endif + iterator erase(iterator); iterator erase(const_iterator); size_type erase(const key_type&); iterator erase(const_iterator, const_iterator); + BOOST_UNORDERED_DEPRECATED("Use erase instead") void quick_erase(const_iterator it) { erase(it); } + BOOST_UNORDERED_DEPRECATED("Use erase instead") void erase_return_void(const_iterator it) { erase(it); } - void clear(); void swap(unordered_multimap&); + // C++17 support: BOOST_NOEXCEPT_IF( + // value_allocator_traits::is_always_equal::value && + // is_nothrow_move_assignable_v && + // is_nothrow_move_assignable_v

) + void clear() BOOST_NOEXCEPT { table_.clear_impl(); } template void merge(boost::unordered_multimap& source); @@ -1089,14 +1274,12 @@ template class unordered_multimap void merge(boost::unordered_multimap&& source); #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template void merge(boost::unordered_map& source); #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) template void merge(boost::unordered_map&& source); -#endif #endif // observers @@ -1170,9 +1353,8 @@ template class unordered_multimap // hash policy - float max_load_factor() const BOOST_NOEXCEPT { return table_.mlf_; } - float load_factor() const BOOST_NOEXCEPT; + float max_load_factor() const BOOST_NOEXCEPT { return table_.mlf_; } void max_load_factor(float) BOOST_NOEXCEPT; void rehash(size_type); void reserve(size_type); @@ -1202,17 +1384,24 @@ unordered_map::unordered_map(size_type n, const hasher& hf, } template -unordered_map::unordered_map( - size_type n, const allocator_type& a) - : table_(n, hasher(), key_equal(), a) +template +unordered_map::unordered_map(InputIt f, InputIt l, size_type n, + const hasher& hf, const key_equal& eql, const allocator_type& a) + : table_(boost::unordered::detail::initial_size(f, l, n), hf, eql, a) { + this->insert(f, l); } template -unordered_map::unordered_map( - size_type n, const hasher& hf, const allocator_type& a) - : table_(n, hf, key_equal(), a) +unordered_map::unordered_map(unordered_map const& other) + : table_(other.table_, + unordered_map::value_allocator_traits:: + select_on_container_copy_construction(other.get_allocator())) { + if (other.table_.size_) { + table_.copy_buckets( + other.table_, boost::unordered::detail::true_type()); + } } template @@ -1227,65 +1416,10 @@ unordered_map::unordered_map( unordered_map const& other, allocator_type const& a) : table_(other.table_, a) { -} - -template -template -unordered_map::unordered_map(InputIt f, InputIt l) - : table_(boost::unordered::detail::initial_size(f, l), hasher(), - key_equal(), allocator_type()) -{ - table_.insert_range(f, l); -} - -template -template -unordered_map::unordered_map( - InputIt f, InputIt l, size_type n, const hasher& hf, const key_equal& eql) - : table_(boost::unordered::detail::initial_size(f, l, n), hf, eql, - allocator_type()) -{ - table_.insert_range(f, l); -} - -template -template -unordered_map::unordered_map(InputIt f, InputIt l, size_type n, - const hasher& hf, const key_equal& eql, const allocator_type& a) - : table_(boost::unordered::detail::initial_size(f, l, n), hf, eql, a) -{ - table_.insert_range(f, l); -} - -template -template -unordered_map::unordered_map(InputIt f, InputIt l, size_type n, - const hasher& hf, const allocator_type& a) - : table_( - boost::unordered::detail::initial_size(f, l, n), hf, key_equal(), a) -{ - table_.insert_range(f, l); -} - -template -template -unordered_map::unordered_map( - InputIt f, InputIt l, size_type n, const allocator_type& a) - : table_(boost::unordered::detail::initial_size(f, l, n), hasher(), - key_equal(), a) -{ - table_.insert_range(f, l); -} - -template -unordered_map::~unordered_map() BOOST_NOEXCEPT -{ -} - -template -unordered_map::unordered_map(unordered_map const& other) - : table_(other.table_) -{ + if (other.table_.size_) { + table_.copy_buckets( + other.table_, boost::unordered::detail::true_type()); + } } template @@ -1293,6 +1427,12 @@ unordered_map::unordered_map( BOOST_RV_REF(unordered_map) other, allocator_type const& a) : table_(other.table_, a, boost::unordered::detail::move_tag()) { + if (table_.node_alloc() == other.table_.node_alloc()) { + table_.move_buckets_from(other.table_); + } else if (other.table_.size_) { + // TODO: Could pick new bucket size? + table_.move_buckets(other.table_); + } } #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) @@ -1305,7 +1445,56 @@ unordered_map::unordered_map( boost::unordered::detail::initial_size(list.begin(), list.end(), n), hf, eql, a) { - table_.insert_range(list.begin(), list.end()); + this->insert(list.begin(), list.end()); +} + +#endif + +template +unordered_map::unordered_map( + size_type n, const allocator_type& a) + : table_(n, hasher(), key_equal(), a) +{ +} + +template +unordered_map::unordered_map( + size_type n, const hasher& hf, const allocator_type& a) + : table_(n, hf, key_equal(), a) +{ +} + +template +template +unordered_map::unordered_map( + InputIt f, InputIt l, size_type n, const allocator_type& a) + : table_(boost::unordered::detail::initial_size(f, l, n), hasher(), + key_equal(), a) +{ + this->insert(f, l); +} + +template +template +unordered_map::unordered_map(InputIt f, InputIt l, size_type n, + const hasher& hf, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(f, l, n), hf, key_equal(), a) +{ + this->insert(f, l); +} + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + +template +unordered_map::unordered_map( + std::initializer_list list, size_type n, + const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(list.begin(), list.end(), n), + hasher(), key_equal(), a) +{ + this->insert(list.begin(), list.end()); } template @@ -1316,26 +1505,24 @@ unordered_map::unordered_map( boost::unordered::detail::initial_size(list.begin(), list.end(), n), hf, key_equal(), a) { - table_.insert_range(list.begin(), list.end()); + this->insert(list.begin(), list.end()); } +#endif + template -unordered_map::unordered_map( - std::initializer_list list, size_type n, - const allocator_type& a) - : table_( - boost::unordered::detail::initial_size(list.begin(), list.end(), n), - hasher(), key_equal(), a) +unordered_map::~unordered_map() BOOST_NOEXCEPT { - table_.insert_range(list.begin(), list.end()); } +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + template unordered_map& unordered_map::operator=( std::initializer_list list) { - table_.clear(); - table_.insert_range(list.begin(), list.end()); + this->clear(); + this->insert(list.begin(), list.end()); return *this; } @@ -1346,7 +1533,13 @@ unordered_map& unordered_map::operator=( template std::size_t unordered_map::max_size() const BOOST_NOEXCEPT { - return table_.max_size(); + using namespace std; + + // size <= mlf_ * count + return boost::unordered::detail::double_to_size( + ceil(static_cast(table_.mlf_) * + static_cast(table_.max_bucket_count()))) - + 1; } // modifiers @@ -1355,7 +1548,10 @@ template template void unordered_map::insert(InputIt first, InputIt last) { - table_.insert_range(first, last); + if (first != last) { + table_.insert_range_unique( + table::extractor::extract(*first), first, last); + } } #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) @@ -1363,39 +1559,56 @@ template void unordered_map::insert( std::initializer_list list) { - table_.insert_range(list.begin(), list.end()); + this->insert(list.begin(), list.end()); } #endif +template +typename unordered_map::iterator +unordered_map::erase(iterator position) +{ + node_pointer node = table::get_node(position); + BOOST_ASSERT(node); + node_pointer next = table::next_node(node); + table_.erase_nodes_unique(node, next); + return iterator(next); +} + template typename unordered_map::iterator unordered_map::erase(const_iterator position) { - return table_.erase(position); + node_pointer node = table::get_node(position); + BOOST_ASSERT(node); + node_pointer next = table::next_node(node); + table_.erase_nodes_unique(node, next); + return iterator(next); } template typename unordered_map::size_type unordered_map::erase(const key_type& k) { - return table_.erase_key(k); + return table_.erase_key_unique(k); } template typename unordered_map::iterator unordered_map::erase(const_iterator first, const_iterator last) { - return table_.erase_range(first, last); -} - -template -void unordered_map::clear() -{ - table_.clear(); + node_pointer last_node = table::get_node(last); + if (first == last) + return iterator(last_node); + table_.erase_nodes_unique(table::get_node(first), last_node); + return iterator(last_node); } template void unordered_map::swap(unordered_map& other) +// C++17 support: BOOST_NOEXCEPT_IF( +// value_allocator_traits::is_always_equal::value && +// is_nothrow_move_assignable_v && +// is_nothrow_move_assignable_v

) { table_.swap(other.table_); } @@ -1405,7 +1618,7 @@ template void unordered_map::merge( boost::unordered_map& source) { - table_.merge_impl(source.table_); + table_.merge_unique(source.table_); } #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) @@ -1414,17 +1627,16 @@ template void unordered_map::merge( boost::unordered_map&& source) { - table_.merge_impl(source.table_); + table_.merge_unique(source.table_); } #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template template void unordered_map::merge( boost::unordered_multimap& source) { - table_.merge_impl(source.table_); + table_.merge_unique(source.table_); } #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) @@ -1433,10 +1645,9 @@ template void unordered_map::merge( boost::unordered_multimap&& source) { - table_.merge_impl(source.table_); + table_.merge_unique(source.table_); } #endif -#endif // observers @@ -1454,27 +1665,6 @@ unordered_map::key_eq() const return table_.key_eq(); } -template -typename unordered_map::mapped_type& - unordered_map::operator[](const key_type& k) -{ - return table_.try_emplace_impl(k).first->second; -} - -template -typename unordered_map::mapped_type& -unordered_map::at(const key_type& k) -{ - return table_.at(k).second; -} - -template -typename unordered_map::mapped_type const& -unordered_map::at(const key_type& k) const -{ - return table_.at(k).second; -} - // lookup template @@ -1497,7 +1687,8 @@ typename unordered_map::iterator unordered_map::find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) { - return iterator(table_.generic_find_node(k, hash, eq)); + return iterator( + table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq)); } template @@ -1506,14 +1697,15 @@ typename unordered_map::const_iterator unordered_map::find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const { - return const_iterator(table_.generic_find_node(k, hash, eq)); + return const_iterator( + table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq)); } template typename unordered_map::size_type unordered_map::count(const key_type& k) const { - return table_.count(k); + return table_.find_node(k) ? 1 : 0; } template @@ -1521,7 +1713,8 @@ std::pair::iterator, typename unordered_map::iterator> unordered_map::equal_range(const key_type& k) { - return table_.equal_range(k); + node_pointer n = table_.find_node(k); + return std::make_pair(iterator(n), iterator(n ? table::next_node(n) : n)); } template @@ -1529,7 +1722,51 @@ std::pair::const_iterator, typename unordered_map::const_iterator> unordered_map::equal_range(const key_type& k) const { - return table_.equal_range(k); + node_pointer n = table_.find_node(k); + return std::make_pair( + const_iterator(n), const_iterator(n ? table::next_node(n) : n)); +} + +template +typename unordered_map::mapped_type& + unordered_map::operator[](const key_type& k) +{ + return table_.try_emplace_unique(k).first->second; +} + +template +typename unordered_map::mapped_type& + unordered_map::operator[](BOOST_RV_REF(key_type) k) +{ + return table_.try_emplace_unique(boost::move(k)).first->second; +} + +template +typename unordered_map::mapped_type& +unordered_map::at(const key_type& k) +{ + if (table_.size_) { + node_pointer n = table_.find_node(k); + if (n) + return n->value().second; + } + + boost::throw_exception( + std::out_of_range("Unable to find key in unordered_map.")); +} + +template +typename unordered_map::mapped_type const& +unordered_map::at(const key_type& k) const +{ + if (table_.size_) { + node_pointer n = table_.find_node(k); + if (n) + return n->value().second; + } + + boost::throw_exception( + std::out_of_range("Unable to find key in unordered_map.")); } template @@ -1544,7 +1781,9 @@ unordered_map::bucket_size(size_type n) const template float unordered_map::load_factor() const BOOST_NOEXCEPT { - return table_.load_factor(); + BOOST_ASSERT(table_.bucket_count_ != 0); + return static_cast(table_.size_) / + static_cast(table_.bucket_count_); } template @@ -1562,7 +1801,8 @@ void unordered_map::rehash(size_type n) template void unordered_map::reserve(size_type n) { - table_.reserve(n); + table_.rehash(static_cast( + std::ceil(static_cast(n) / table_.mlf_))); } template @@ -1575,7 +1815,7 @@ inline bool operator==(unordered_map const& m1, unordered_map x; }; #endif - return m1.table_.equals(m2.table_); + return m1.table_.equals_unique(m2.table_); } template @@ -1588,12 +1828,13 @@ inline bool operator!=(unordered_map const& m1, unordered_map x; }; #endif - return !m1.table_.equals(m2.table_); + return !m1.table_.equals_unique(m2.table_); } template inline void swap( unordered_map& m1, unordered_map& m2) + BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2))) { #if BOOST_WORKAROUND(__CODEGEARC__, BOOST_TESTED_AT(0x0613)) struct dummy @@ -1621,17 +1862,26 @@ unordered_multimap::unordered_multimap(size_type n, } template -unordered_multimap::unordered_multimap( - size_type n, const allocator_type& a) - : table_(n, hasher(), key_equal(), a) +template +unordered_multimap::unordered_multimap(InputIt f, InputIt l, + size_type n, const hasher& hf, const key_equal& eql, + const allocator_type& a) + : table_(boost::unordered::detail::initial_size(f, l, n), hf, eql, a) { + this->insert(f, l); } template unordered_multimap::unordered_multimap( - size_type n, const hasher& hf, const allocator_type& a) - : table_(n, hf, key_equal(), a) + unordered_multimap const& other) + : table_(other.table_, + unordered_multimap::value_allocator_traits:: + select_on_container_copy_construction(other.get_allocator())) { + if (other.table_.size_) { + table_.copy_buckets( + other.table_, boost::unordered::detail::false_type()); + } } template @@ -1646,67 +1896,10 @@ unordered_multimap::unordered_multimap( unordered_multimap const& other, allocator_type const& a) : table_(other.table_, a) { -} - -template -template -unordered_multimap::unordered_multimap(InputIt f, InputIt l) - : table_(boost::unordered::detail::initial_size(f, l), hasher(), - key_equal(), allocator_type()) -{ - table_.insert_range(f, l); -} - -template -template -unordered_multimap::unordered_multimap( - InputIt f, InputIt l, size_type n, const hasher& hf, const key_equal& eql) - : table_(boost::unordered::detail::initial_size(f, l, n), hf, eql, - allocator_type()) -{ - table_.insert_range(f, l); -} - -template -template -unordered_multimap::unordered_multimap(InputIt f, InputIt l, - size_type n, const hasher& hf, const key_equal& eql, - const allocator_type& a) - : table_(boost::unordered::detail::initial_size(f, l, n), hf, eql, a) -{ - table_.insert_range(f, l); -} - -template -template -unordered_multimap::unordered_multimap(InputIt f, InputIt l, - size_type n, const hasher& hf, const allocator_type& a) - : table_( - boost::unordered::detail::initial_size(f, l, n), hf, key_equal(), a) -{ - table_.insert_range(f, l); -} - -template -template -unordered_multimap::unordered_multimap( - InputIt f, InputIt l, size_type n, const allocator_type& a) - : table_(boost::unordered::detail::initial_size(f, l, n), hasher(), - key_equal(), a) -{ - table_.insert_range(f, l); -} - -template -unordered_multimap::~unordered_multimap() BOOST_NOEXCEPT -{ -} - -template -unordered_multimap::unordered_multimap( - unordered_multimap const& other) - : table_(other.table_) -{ + if (other.table_.size_) { + table_.copy_buckets( + other.table_, boost::unordered::detail::false_type()); + } } template @@ -1714,6 +1907,12 @@ unordered_multimap::unordered_multimap( BOOST_RV_REF(unordered_multimap) other, allocator_type const& a) : table_(other.table_, a, boost::unordered::detail::move_tag()) { + if (table_.node_alloc() == other.table_.node_alloc()) { + table_.move_buckets_from(other.table_); + } else if (other.table_.size_) { + // TODO: Could pick new bucket size? + table_.move_buckets_equiv(other.table_); + } } #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) @@ -1726,7 +1925,56 @@ unordered_multimap::unordered_multimap( boost::unordered::detail::initial_size(list.begin(), list.end(), n), hf, eql, a) { - table_.insert_range(list.begin(), list.end()); + this->insert(list.begin(), list.end()); +} + +#endif + +template +unordered_multimap::unordered_multimap( + size_type n, const allocator_type& a) + : table_(n, hasher(), key_equal(), a) +{ +} + +template +unordered_multimap::unordered_multimap( + size_type n, const hasher& hf, const allocator_type& a) + : table_(n, hf, key_equal(), a) +{ +} + +template +template +unordered_multimap::unordered_multimap( + InputIt f, InputIt l, size_type n, const allocator_type& a) + : table_(boost::unordered::detail::initial_size(f, l, n), hasher(), + key_equal(), a) +{ + this->insert(f, l); +} + +template +template +unordered_multimap::unordered_multimap(InputIt f, InputIt l, + size_type n, const hasher& hf, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(f, l, n), hf, key_equal(), a) +{ + this->insert(f, l); +} + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + +template +unordered_multimap::unordered_multimap( + std::initializer_list list, size_type n, + const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(list.begin(), list.end(), n), + hasher(), key_equal(), a) +{ + this->insert(list.begin(), list.end()); } template @@ -1737,26 +1985,24 @@ unordered_multimap::unordered_multimap( boost::unordered::detail::initial_size(list.begin(), list.end(), n), hf, key_equal(), a) { - table_.insert_range(list.begin(), list.end()); + this->insert(list.begin(), list.end()); } +#endif + template -unordered_multimap::unordered_multimap( - std::initializer_list list, size_type n, - const allocator_type& a) - : table_( - boost::unordered::detail::initial_size(list.begin(), list.end(), n), - hasher(), key_equal(), a) +unordered_multimap::~unordered_multimap() BOOST_NOEXCEPT { - table_.insert_range(list.begin(), list.end()); } +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + template unordered_multimap& unordered_multimap::operator=( std::initializer_list list) { - table_.clear(); - table_.insert_range(list.begin(), list.end()); + this->clear(); + this->insert(list.begin(), list.end()); return *this; } @@ -1767,7 +2013,13 @@ unordered_multimap& unordered_multimap::operator=( template std::size_t unordered_multimap::max_size() const BOOST_NOEXCEPT { - return table_.max_size(); + using namespace std; + + // size <= mlf_ * count + return boost::unordered::detail::double_to_size( + ceil(static_cast(table_.mlf_) * + static_cast(table_.max_bucket_count()))) - + 1; } // modifiers @@ -1776,7 +2028,7 @@ template template void unordered_multimap::insert(InputIt first, InputIt last) { - table_.insert_range(first, last); + table_.insert_range_equiv(first, last); } #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) @@ -1784,22 +2036,37 @@ template void unordered_multimap::insert( std::initializer_list list) { - table_.insert_range(list.begin(), list.end()); + this->insert(list.begin(), list.end()); } #endif +template +typename unordered_multimap::iterator +unordered_multimap::erase(iterator position) +{ + node_pointer node = table::get_node(position); + BOOST_ASSERT(node); + node_pointer next = table::next_node(node); + table_.erase_nodes_equiv(node, next); + return iterator(next); +} + template typename unordered_multimap::iterator unordered_multimap::erase(const_iterator position) { - return table_.erase(position); + node_pointer node = table::get_node(position); + BOOST_ASSERT(node); + node_pointer next = table::next_node(node); + table_.erase_nodes_equiv(node, next); + return iterator(next); } template typename unordered_multimap::size_type unordered_multimap::erase(const key_type& k) { - return table_.erase_key(k); + return table_.erase_key_equiv(k); } template @@ -1807,17 +2074,19 @@ typename unordered_multimap::iterator unordered_multimap::erase( const_iterator first, const_iterator last) { - return table_.erase_range(first, last); -} - -template -void unordered_multimap::clear() -{ - table_.clear(); + node_pointer last_node = table::get_node(last); + if (first == last) + return iterator(last_node); + table_.erase_nodes_equiv(table::get_node(first), last_node); + return iterator(last_node); } template void unordered_multimap::swap(unordered_multimap& other) +// C++17 support: BOOST_NOEXCEPT_IF( +// value_allocator_traits::is_always_equal::value && +// is_nothrow_move_assignable_v && +// is_nothrow_move_assignable_v

) { table_.swap(other.table_); } @@ -1860,7 +2129,6 @@ void unordered_multimap::merge( } #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template template void unordered_multimap::merge( @@ -1882,7 +2150,6 @@ void unordered_multimap::merge( } } #endif -#endif // lookup @@ -1906,7 +2173,8 @@ typename unordered_multimap::iterator unordered_multimap::find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) { - return iterator(table_.generic_find_node(k, hash, eq)); + return iterator( + table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq)); } template @@ -1915,14 +2183,16 @@ typename unordered_multimap::const_iterator unordered_multimap::find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const { - return const_iterator(table_.generic_find_node(k, hash, eq)); + return const_iterator( + table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq)); } template typename unordered_multimap::size_type unordered_multimap::count(const key_type& k) const { - return table_.count(k); + node_pointer n = table_.find_node(k); + return n ? table_.group_count(n) : 0; } template @@ -1930,7 +2200,8 @@ std::pair::iterator, typename unordered_multimap::iterator> unordered_multimap::equal_range(const key_type& k) { - return table_.equal_range(k); + node_pointer n = table_.find_node(k); + return std::make_pair(iterator(n), iterator(n ? table_.next_group(n) : n)); } template @@ -1938,7 +2209,9 @@ std::pair::const_iterator, typename unordered_multimap::const_iterator> unordered_multimap::equal_range(const key_type& k) const { - return table_.equal_range(k); + node_pointer n = table_.find_node(k); + return std::make_pair( + const_iterator(n), const_iterator(n ? table_.next_group(n) : n)); } template @@ -1953,7 +2226,9 @@ unordered_multimap::bucket_size(size_type n) const template float unordered_multimap::load_factor() const BOOST_NOEXCEPT { - return table_.load_factor(); + BOOST_ASSERT(table_.bucket_count_ != 0); + return static_cast(table_.size_) / + static_cast(table_.bucket_count_); } template @@ -1971,7 +2246,8 @@ void unordered_multimap::rehash(size_type n) template void unordered_multimap::reserve(size_type n) { - table_.reserve(n); + table_.rehash(static_cast( + std::ceil(static_cast(n) / table_.mlf_))); } template @@ -1984,7 +2260,7 @@ inline bool operator==(unordered_multimap const& m1, unordered_multimap x; }; #endif - return m1.table_.equals(m2.table_); + return m1.table_.equals_equiv(m2.table_); } template @@ -1997,12 +2273,13 @@ inline bool operator!=(unordered_multimap const& m1, unordered_multimap x; }; #endif - return !m1.table_.equals(m2.table_); + return !m1.table_.equals_equiv(m2.table_); } template inline void swap(unordered_multimap& m1, unordered_multimap& m2) + BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2))) { #if BOOST_WORKAROUND(__CODEGEARC__, BOOST_TESTED_AT(0x0613)) struct dummy @@ -2017,10 +2294,11 @@ template class node_handle_map { BOOST_MOVABLE_BUT_NOT_COPYABLE(node_handle_map) - template - friend struct ::boost::unordered::detail::table_impl; - template - friend struct ::boost::unordered::detail::grouped_table_impl; + template friend struct ::boost::unordered::detail::table; + template + friend class boost::unordered::unordered_map; + template + friend class boost::unordered::unordered_multimap; typedef typename boost::unordered::detail::rebind_wrap >::type value_allocator; @@ -2043,13 +2321,7 @@ template class node_handle_map bool has_alloc_; boost::unordered::detail::value_base alloc_; - public: - BOOST_CONSTEXPR node_handle_map() BOOST_NOEXCEPT : ptr_(), has_alloc_(false) - { - } - - /*BOOST_CONSTEXPR */ node_handle_map( - node_pointer ptr, allocator_type const& a) + node_handle_map(node_pointer ptr, allocator_type const& a) : ptr_(ptr), has_alloc_(false) { if (ptr_) { @@ -2058,6 +2330,11 @@ template class node_handle_map } } + public: + BOOST_CONSTEXPR node_handle_map() BOOST_NOEXCEPT : ptr_(), has_alloc_(false) + { + } + ~node_handle_map() { if (has_alloc_ && ptr_) { diff --git a/include/boost/unordered/unordered_map_fwd.hpp b/include/boost/unordered/unordered_map_fwd.hpp index 12855e92..ebac59b4 100644 --- a/include/boost/unordered/unordered_map_fwd.hpp +++ b/include/boost/unordered/unordered_map_fwd.hpp @@ -30,7 +30,9 @@ template inline bool operator!=( unordered_map const&, unordered_map const&); template -inline void swap(unordered_map&, unordered_map&); +inline void swap( + unordered_map& m1, unordered_map& m2) + BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2))); template , class P = std::equal_to, @@ -44,8 +46,9 @@ template inline bool operator!=(unordered_multimap const&, unordered_multimap const&); template -inline void swap( - unordered_multimap&, unordered_multimap&); +inline void swap(unordered_multimap& m1, + unordered_multimap& m2) + BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2))); template class node_handle_map; template struct insert_return_type_map; diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 40d566c2..985742ff 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -53,6 +53,8 @@ template class unordered_set typedef boost::unordered::detail::set types; typedef typename types::value_allocator_traits value_allocator_traits; typedef typename types::table table; + typedef typename table::node_pointer node_pointer; + typedef typename table::link_pointer link_pointer; public: typedef typename value_allocator_traits::pointer pointer; @@ -64,10 +66,10 @@ template class unordered_set typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; - typedef typename table::cl_iterator const_local_iterator; - typedef typename table::l_iterator local_iterator; - typedef typename table::c_iterator const_iterator; typedef typename table::iterator iterator; + typedef typename table::c_iterator const_iterator; + typedef typename table::l_iterator local_iterator; + typedef typename table::cl_iterator const_local_iterator; typedef typename types::node_type node_type; typedef typename types::insert_return_type insert_return_type; @@ -83,59 +85,54 @@ template class unordered_set const key_equal& = key_equal(), const allocator_type& = allocator_type()); - explicit unordered_set(size_type, const allocator_type&); - - explicit unordered_set(size_type, const hasher&, const allocator_type&); - - explicit unordered_set(allocator_type const&); - - template unordered_set(InputIt, InputIt); - template - unordered_set(InputIt, InputIt, size_type, const hasher& = hasher(), - const key_equal& = key_equal()); - - template - unordered_set(InputIt, InputIt, size_type, const hasher&, const key_equal&, - const allocator_type&); - - template - unordered_set( - InputIt, InputIt, size_type, const hasher&, const allocator_type&); - - template - unordered_set(InputIt, InputIt, size_type, const allocator_type&); - - // copy/move constructors + unordered_set(InputIt, InputIt, + size_type = boost::unordered::detail::default_bucket_count, + const hasher& = hasher(), const key_equal& = key_equal(), + const allocator_type& = allocator_type()); unordered_set(unordered_set const&); - unordered_set(unordered_set const&, allocator_type const&); - unordered_set(BOOST_RV_REF(unordered_set), allocator_type const&); - -#if defined(BOOST_UNORDERED_USE_MOVE) +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) unordered_set(BOOST_RV_REF(unordered_set) other) BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) : table_(other.table_, boost::unordered::detail::move_tag()) { - } -#elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) - unordered_set(unordered_set&& other) - BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) - : table_(other.table_, boost::unordered::detail::move_tag()) - { + // The move is done in table_ } #endif + explicit unordered_set(allocator_type const&); + + unordered_set(unordered_set const&, allocator_type const&); + + unordered_set(BOOST_RV_REF(unordered_set), allocator_type const&); + #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) unordered_set(std::initializer_list, size_type = boost::unordered::detail::default_bucket_count, const hasher& = hasher(), const key_equal& l = key_equal(), const allocator_type& = allocator_type()); - unordered_set(std::initializer_list, size_type, const hasher&, - const allocator_type&); +#endif + + explicit unordered_set(size_type, const allocator_type&); + + explicit unordered_set(size_type, const hasher&, const allocator_type&); + + template + unordered_set(InputIt, InputIt, size_type, const allocator_type&); + + template + unordered_set( + InputIt, InputIt, size_type, const hasher&, const allocator_type&); + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) unordered_set( std::initializer_list, size_type, const allocator_type&); + + unordered_set(std::initializer_list, size_type, const hasher&, + const allocator_type&); #endif // Destructor @@ -147,26 +144,34 @@ template class unordered_set #if defined(BOOST_UNORDERED_USE_MOVE) unordered_set& operator=(BOOST_COPY_ASSIGN_REF(unordered_set) x) { - table_.assign(x.table_); + table_.assign(x.table_, boost::unordered::detail::true_type()); return *this; } unordered_set& operator=(BOOST_RV_REF(unordered_set) x) + // C++17 support: BOOST_NOEXCEPT_IF( + // value_allocator_traits::is_always_equal::value && + // is_nothrow_move_assignable_v && + // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, boost::unordered::detail::true_type()); return *this; } #else unordered_set& operator=(unordered_set const& x) { - table_.assign(x.table_); + table_.assign(x.table_, boost::unordered::detail::true_type()); return *this; } #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) unordered_set& operator=(unordered_set&& x) + // C++17 support: BOOST_NOEXCEPT_IF( + // value_allocator_traits::is_always_equal::value && + // is_nothrow_move_assignable_v && + // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, boost::unordered::detail::true_type()); return *this; } #endif @@ -181,14 +186,6 @@ template class unordered_set return table_.node_alloc(); } - // size and capacity - - bool empty() const BOOST_NOEXCEPT { return table_.size_ == 0; } - - size_type size() const BOOST_NOEXCEPT { return table_.size_; } - - size_type max_size() const BOOST_NOEXCEPT; - // iterators iterator begin() BOOST_NOEXCEPT { return iterator(table_.begin()); } @@ -209,36 +206,29 @@ template class unordered_set const_iterator cend() const BOOST_NOEXCEPT { return const_iterator(); } - // extract + // size and capacity - node_type extract(const_iterator position) - { - return node_type( - table_.extract_by_iterator(position), table_.node_alloc()); - } + bool empty() const BOOST_NOEXCEPT { return table_.size_ == 0; } - node_type extract(const key_type& k) - { - return node_type(table_.extract_by_key(k), table_.node_alloc()); - } + size_type size() const BOOST_NOEXCEPT { return table_.size_; } + + size_type max_size() const BOOST_NOEXCEPT; // emplace #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + template std::pair emplace(BOOST_FWD_REF(Args)... args) { - return table_.emplace(boost::forward(args)...); + return table_.emplace_unique( + table::extractor::extract(boost::forward(args)...), + boost::forward(args)...); } - template - iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args) - { - return table_.emplace_hint(hint, boost::forward(args)...); - } #else -#if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100)) +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 // 0 argument emplace requires special treatment in case // the container is instantiated with a value type that @@ -252,6 +242,56 @@ template class unordered_set return this->emplace(boost::move(v)); } +#endif + + template + std::pair emplace(BOOST_FWD_REF(A0) a0) + { + return table_.emplace_unique( + table::extractor::extract(boost::forward(a0)), + boost::unordered::detail::create_emplace_args( + boost::forward(a0))); + } + + template + std::pair emplace( + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return table_.emplace_unique( + table::extractor::extract( + boost::forward(a0), boost::forward(a1)), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))); + } + + template + std::pair emplace( + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return table_.emplace_unique( + table::extractor::extract( + boost::forward(a0), boost::forward(a1)), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))); + } + +#endif + +#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + template + iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args) + { + return table_.emplace_hint_unique(hint, + table::extractor::extract(boost::forward(args)...), + boost::forward(args)...); + } + +#else + +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 + iterator emplace_hint(const_iterator hint, boost::unordered::detail::empty_emplace = boost::unordered::detail::empty_emplace(), @@ -262,77 +302,73 @@ template class unordered_set #endif - template - std::pair emplace(BOOST_FWD_REF(A0) a0) - { - return table_.emplace(boost::unordered::detail::create_emplace_args( - boost::forward(a0))); - } - template iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(A0) a0) { - return table_.emplace_hint( - hint, boost::unordered::detail::create_emplace_args( - boost::forward(a0))); - } - - template - std::pair emplace( - BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) - { - return table_.emplace(boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1))); + return table_.emplace_hint_unique(hint, + table::extractor::extract(boost::forward(a0)), + boost::unordered::detail::create_emplace_args( + boost::forward(a0))); } template iterator emplace_hint( const_iterator hint, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) { - return table_.emplace_hint( - hint, boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1))); - } - - template - std::pair emplace( - BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) - { - return table_.emplace(boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1), - boost::forward(a2))); + return table_.emplace_hint_unique( + hint, table::extractor::extract( + boost::forward(a0), boost::forward(a1)), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))); } template iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) { - return table_.emplace_hint( - hint, boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1), - boost::forward(a2))); + return table_.emplace_hint_unique( + hint, table::extractor::extract( + boost::forward(a0), boost::forward(a1)), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))); } +#endif + +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + #define BOOST_UNORDERED_EMPLACE(z, n, _) \ template \ std::pair emplace( \ BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ { \ - return table_.emplace(boost::unordered::detail::create_emplace_args( \ - BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + return table_.emplace_unique( \ + table::extractor::extract( \ + boost::forward(a0), boost::forward(a1)), \ + boost::unordered::detail::create_emplace_args( \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ } \ \ template \ iterator emplace_hint(const_iterator hint, \ BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ { \ - return table_.emplace_hint( \ - hint, boost::unordered::detail::create_emplace_args( \ - BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + return table_.emplace_hint_unique( \ + hint, table::extractor::extract( \ + boost::forward(a0), boost::forward(a1)), \ + boost::unordered::detail::create_emplace_args( \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ } - BOOST_PP_REPEAT_FROM_TO( - 4, BOOST_UNORDERED_EMPLACE_LIMIT, BOOST_UNORDERED_EMPLACE, _) + BOOST_UNORDERED_EMPLACE(1, 4, _) + BOOST_UNORDERED_EMPLACE(1, 5, _) + BOOST_UNORDERED_EMPLACE(1, 6, _) + BOOST_UNORDERED_EMPLACE(1, 7, _) + BOOST_UNORDERED_EMPLACE(1, 8, _) + BOOST_UNORDERED_EMPLACE(1, 9, _) + BOOST_PP_REPEAT_FROM_TO(10, BOOST_PP_INC(BOOST_UNORDERED_EMPLACE_LIMIT), + BOOST_UNORDERED_EMPLACE, _) #undef BOOST_UNORDERED_EMPLACE @@ -364,19 +400,33 @@ template class unordered_set void insert(std::initializer_list); #endif + // extract + + node_type extract(const_iterator position) + { + return node_type( + table_.extract_by_iterator_unique(position), table_.node_alloc()); + } + + node_type extract(const key_type& k) + { + return node_type(table_.extract_by_key(k), table_.node_alloc()); + } + insert_return_type insert(BOOST_RV_REF(node_type) np) { insert_return_type result; - table_.move_insert_node_type(np, result); + table_.move_insert_node_type_unique(np, result); return boost::move(result); } iterator insert(const_iterator hint, BOOST_RV_REF(node_type) np) { - return table_.move_insert_node_type_with_hint(hint, np); + return table_.move_insert_node_type_with_hint_unique(hint, np); } -#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || \ + (BOOST_COMP_GNUC && BOOST_COMP_GNUC < BOOST_VERSION_NUMBER(4, 6, 0)) private: // Note: Use r-value node_type to insert. insert_return_type insert(node_type&); @@ -388,11 +438,17 @@ template class unordered_set iterator erase(const_iterator); size_type erase(const key_type&); iterator erase(const_iterator, const_iterator); + BOOST_UNORDERED_DEPRECATED("Use erase instead") void quick_erase(const_iterator it) { erase(it); } + BOOST_UNORDERED_DEPRECATED("Use erase instead") void erase_return_void(const_iterator it) { erase(it); } - void clear(); void swap(unordered_set&); + // C++17 support: BOOST_NOEXCEPT_IF( + // value_allocator_traits::is_always_equal::value && + // is_nothrow_move_assignable_v && + // is_nothrow_move_assignable_v

) + void clear() BOOST_NOEXCEPT { table_.clear_impl(); } template void merge(boost::unordered_set& source); @@ -402,14 +458,12 @@ template class unordered_set void merge(boost::unordered_set&& source); #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template void merge(boost::unordered_multiset& source); #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) template void merge(boost::unordered_multiset&& source); -#endif #endif // observers @@ -476,9 +530,8 @@ template class unordered_set // hash policy - float max_load_factor() const BOOST_NOEXCEPT { return table_.mlf_; } - float load_factor() const BOOST_NOEXCEPT; + float max_load_factor() const BOOST_NOEXCEPT { return table_.mlf_; } void max_load_factor(float) BOOST_NOEXCEPT; void rehash(size_type); void reserve(size_type); @@ -507,9 +560,11 @@ template class unordered_multiset typedef A allocator_type; private: - typedef boost::unordered::detail::multiset types; + typedef boost::unordered::detail::set types; typedef typename types::value_allocator_traits value_allocator_traits; typedef typename types::table table; + typedef typename table::node_pointer node_pointer; + typedef typename table::link_pointer link_pointer; public: typedef typename value_allocator_traits::pointer pointer; @@ -521,10 +576,10 @@ template class unordered_multiset typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; - typedef typename table::cl_iterator const_local_iterator; - typedef typename table::l_iterator local_iterator; - typedef typename table::c_iterator const_iterator; typedef typename table::iterator iterator; + typedef typename table::c_iterator const_iterator; + typedef typename table::l_iterator local_iterator; + typedef typename table::cl_iterator const_local_iterator; typedef typename types::node_type node_type; private: @@ -539,60 +594,55 @@ template class unordered_multiset const key_equal& = key_equal(), const allocator_type& = allocator_type()); - explicit unordered_multiset(size_type, const allocator_type&); - - explicit unordered_multiset( - size_type, const hasher&, const allocator_type&); - - explicit unordered_multiset(allocator_type const&); - - template unordered_multiset(InputIt, InputIt); - template - unordered_multiset(InputIt, InputIt, size_type, const hasher& = hasher(), - const key_equal& = key_equal()); - - template - unordered_multiset(InputIt, InputIt, size_type, const hasher&, - const key_equal&, const allocator_type&); - - template - unordered_multiset( - InputIt, InputIt, size_type, const hasher&, const allocator_type&); - - template - unordered_multiset(InputIt, InputIt, size_type, const allocator_type&); - - // copy/move constructors + unordered_multiset(InputIt, InputIt, + size_type = boost::unordered::detail::default_bucket_count, + const hasher& = hasher(), const key_equal& = key_equal(), + const allocator_type& = allocator_type()); unordered_multiset(unordered_multiset const&); - unordered_multiset(unordered_multiset const&, allocator_type const&); - unordered_multiset(BOOST_RV_REF(unordered_multiset), allocator_type const&); - -#if defined(BOOST_UNORDERED_USE_MOVE) +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) unordered_multiset(BOOST_RV_REF(unordered_multiset) other) BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) : table_(other.table_, boost::unordered::detail::move_tag()) { - } -#elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) - unordered_multiset(unordered_multiset&& other) - BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) - : table_(other.table_, boost::unordered::detail::move_tag()) - { + // The move is done in table_ } #endif + explicit unordered_multiset(allocator_type const&); + + unordered_multiset(unordered_multiset const&, allocator_type const&); + + unordered_multiset(BOOST_RV_REF(unordered_multiset), allocator_type const&); + #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) unordered_multiset(std::initializer_list, size_type = boost::unordered::detail::default_bucket_count, const hasher& = hasher(), const key_equal& l = key_equal(), const allocator_type& = allocator_type()); - unordered_multiset(std::initializer_list, size_type, - const hasher&, const allocator_type&); +#endif + + explicit unordered_multiset(size_type, const allocator_type&); + + explicit unordered_multiset( + size_type, const hasher&, const allocator_type&); + + template + unordered_multiset(InputIt, InputIt, size_type, const allocator_type&); + + template + unordered_multiset( + InputIt, InputIt, size_type, const hasher&, const allocator_type&); + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) unordered_multiset( std::initializer_list, size_type, const allocator_type&); + + unordered_multiset(std::initializer_list, size_type, + const hasher&, const allocator_type&); #endif // Destructor @@ -604,26 +654,34 @@ template class unordered_multiset #if defined(BOOST_UNORDERED_USE_MOVE) unordered_multiset& operator=(BOOST_COPY_ASSIGN_REF(unordered_multiset) x) { - table_.assign(x.table_); + table_.assign(x.table_, boost::unordered::detail::false_type()); return *this; } unordered_multiset& operator=(BOOST_RV_REF(unordered_multiset) x) + // C++17 support: BOOST_NOEXCEPT_IF( + // value_allocator_traits::is_always_equal::value && + // is_nothrow_move_assignable_v && + // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, boost::unordered::detail::false_type()); return *this; } #else unordered_multiset& operator=(unordered_multiset const& x) { - table_.assign(x.table_); + table_.assign(x.table_, boost::unordered::detail::false_type()); return *this; } #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) unordered_multiset& operator=(unordered_multiset&& x) + // C++17 support: BOOST_NOEXCEPT_IF( + // value_allocator_traits::is_always_equal::value && + // is_nothrow_move_assignable_v && + // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, boost::unordered::detail::false_type()); return *this; } #endif @@ -638,14 +696,6 @@ template class unordered_multiset return table_.node_alloc(); } - // size and capacity - - bool empty() const BOOST_NOEXCEPT { return table_.size_ == 0; } - - size_type size() const BOOST_NOEXCEPT { return table_.size_; } - - size_type max_size() const BOOST_NOEXCEPT; - // iterators iterator begin() BOOST_NOEXCEPT { return iterator(table_.begin()); } @@ -666,35 +716,28 @@ template class unordered_multiset const_iterator cend() const BOOST_NOEXCEPT { return const_iterator(); } - // extract + // size and capacity - node_type extract(const_iterator position) - { - return node_type( - table_.extract_by_iterator(position), table_.node_alloc()); - } + bool empty() const BOOST_NOEXCEPT { return table_.size_ == 0; } - node_type extract(const key_type& k) - { - return node_type(table_.extract_by_key(k), table_.node_alloc()); - } + size_type size() const BOOST_NOEXCEPT { return table_.size_; } + + size_type max_size() const BOOST_NOEXCEPT; // emplace #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + template iterator emplace(BOOST_FWD_REF(Args)... args) { - return table_.emplace(boost::forward(args)...); + return iterator(table_.emplace_equiv( + boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), boost::forward(args)...))); } - template - iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args) - { - return table_.emplace_hint(hint, boost::forward(args)...); - } #else -#if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100)) +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 // 0 argument emplace requires special treatment in case // the container is instantiated with a value type that @@ -707,6 +750,55 @@ template class unordered_multiset return this->emplace(boost::move(v)); } +#endif + + template iterator emplace(BOOST_FWD_REF(A0) a0) + { + return iterator(table_.emplace_equiv( + boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), + boost::unordered::detail::create_emplace_args( + boost::forward(a0))))); + } + + template + iterator emplace(BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return iterator(table_.emplace_equiv( + boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))))); + } + + template + iterator emplace( + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return iterator(table_.emplace_equiv( + boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))))); + } + +#endif + +#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + template + iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args) + { + return iterator(table_.emplace_hint_equiv( + hint, boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), boost::forward(args)...))); + } + +#else + +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 + iterator emplace_hint(const_iterator hint, boost::unordered::detail::empty_emplace = boost::unordered::detail::empty_emplace(), @@ -717,74 +809,74 @@ template class unordered_multiset #endif - template iterator emplace(BOOST_FWD_REF(A0) a0) - { - return table_.emplace(boost::unordered::detail::create_emplace_args( - boost::forward(a0))); - } - template iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(A0) a0) { - return table_.emplace_hint( - hint, boost::unordered::detail::create_emplace_args( - boost::forward(a0))); - } - - template - iterator emplace(BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) - { - return table_.emplace(boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1))); + return iterator(table_.emplace_hint_equiv( + hint, boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), + boost::unordered::detail::create_emplace_args( + boost::forward(a0))))); } template iterator emplace_hint( const_iterator hint, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) { - return table_.emplace_hint( - hint, boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1))); - } - - template - iterator emplace( - BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) - { - return table_.emplace(boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1), - boost::forward(a2))); + return iterator(table_.emplace_hint_equiv( + hint, boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))))); } template iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) { - return table_.emplace_hint( - hint, boost::unordered::detail::create_emplace_args( - boost::forward(a0), boost::forward(a1), - boost::forward(a2))); + return iterator(table_.emplace_hint_equiv( + hint, boost::unordered::detail::func::construct_node_from_args( + table_.node_alloc(), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))))); } +#endif + +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + #define BOOST_UNORDERED_EMPLACE(z, n, _) \ template \ iterator emplace(BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ { \ - return table_.emplace(boost::unordered::detail::create_emplace_args( \ - BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + return iterator(table_.emplace_equiv( \ + boost::unordered::detail::func::construct_node_from_args( \ + table_.node_alloc(), \ + boost::unordered::detail::create_emplace_args( \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))))); \ } \ \ template \ iterator emplace_hint(const_iterator hint, \ BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ { \ - return table_.emplace_hint( \ - hint, boost::unordered::detail::create_emplace_args( \ - BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + return iterator(table_.emplace_hint_equiv( \ + hint, \ + boost::unordered::detail::func::construct_node_from_args( \ + table_.node_alloc(), \ + boost::unordered::detail::create_emplace_args( \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))))); \ } - BOOST_PP_REPEAT_FROM_TO( - 4, BOOST_UNORDERED_EMPLACE_LIMIT, BOOST_UNORDERED_EMPLACE, _) + BOOST_UNORDERED_EMPLACE(1, 4, _) + BOOST_UNORDERED_EMPLACE(1, 5, _) + BOOST_UNORDERED_EMPLACE(1, 6, _) + BOOST_UNORDERED_EMPLACE(1, 7, _) + BOOST_UNORDERED_EMPLACE(1, 8, _) + BOOST_UNORDERED_EMPLACE(1, 9, _) + BOOST_PP_REPEAT_FROM_TO(10, BOOST_PP_INC(BOOST_UNORDERED_EMPLACE_LIMIT), + BOOST_UNORDERED_EMPLACE, _) #undef BOOST_UNORDERED_EMPLACE @@ -813,17 +905,31 @@ template class unordered_multiset void insert(std::initializer_list); #endif + // extract + + node_type extract(const_iterator position) + { + return node_type( + table_.extract_by_iterator_equiv(position), table_.node_alloc()); + } + + node_type extract(const key_type& k) + { + return node_type(table_.extract_by_key(k), table_.node_alloc()); + } + iterator insert(BOOST_RV_REF(node_type) np) { - return table_.move_insert_node_type(np); + return table_.move_insert_node_type_equiv(np); } iterator insert(const_iterator hint, BOOST_RV_REF(node_type) np) { - return table_.move_insert_node_type_with_hint(hint, np); + return table_.move_insert_node_type_with_hint_equiv(hint, np); } -#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || \ + (BOOST_COMP_GNUC && BOOST_COMP_GNUC < BOOST_VERSION_NUMBER(4, 6, 0)) private: // Note: Use r-value node_type to insert. iterator insert(node_type&); @@ -835,11 +941,17 @@ template class unordered_multiset iterator erase(const_iterator); size_type erase(const key_type&); iterator erase(const_iterator, const_iterator); + BOOST_UNORDERED_DEPRECATED("Use erase instead") void quick_erase(const_iterator it) { erase(it); } + BOOST_UNORDERED_DEPRECATED("Use erase instead") void erase_return_void(const_iterator it) { erase(it); } - void clear(); void swap(unordered_multiset&); + // C++17 support: BOOST_NOEXCEPT_IF( + // value_allocator_traits::is_always_equal::value && + // is_nothrow_move_assignable_v && + // is_nothrow_move_assignable_v

) + void clear() BOOST_NOEXCEPT { table_.clear_impl(); } template void merge(boost::unordered_multiset& source); @@ -849,14 +961,12 @@ template class unordered_multiset void merge(boost::unordered_multiset&& source); #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template void merge(boost::unordered_set& source); #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) template void merge(boost::unordered_set&& source); -#endif #endif // observers @@ -923,9 +1033,8 @@ template class unordered_multiset // hash policy - float max_load_factor() const BOOST_NOEXCEPT { return table_.mlf_; } - float load_factor() const BOOST_NOEXCEPT; + float max_load_factor() const BOOST_NOEXCEPT { return table_.mlf_; } void max_load_factor(float) BOOST_NOEXCEPT; void rehash(size_type); void reserve(size_type); @@ -939,7 +1048,6 @@ template class unordered_multiset }; // class template unordered_multiset //////////////////////////////////////////////////////////////////////////////// - template unordered_set::unordered_set() : table_(boost::unordered::detail::default_bucket_count, hasher(), @@ -955,16 +1063,24 @@ unordered_set::unordered_set(size_type n, const hasher& hf, } template -unordered_set::unordered_set(size_type n, const allocator_type& a) - : table_(n, hasher(), key_equal(), a) +template +unordered_set::unordered_set(InputIt f, InputIt l, size_type n, + const hasher& hf, const key_equal& eql, const allocator_type& a) + : table_(boost::unordered::detail::initial_size(f, l, n), hf, eql, a) { + this->insert(f, l); } template -unordered_set::unordered_set( - size_type n, const hasher& hf, const allocator_type& a) - : table_(n, hf, key_equal(), a) +unordered_set::unordered_set(unordered_set const& other) + : table_(other.table_, + unordered_set::value_allocator_traits:: + select_on_container_copy_construction(other.get_allocator())) { + if (other.table_.size_) { + table_.copy_buckets( + other.table_, boost::unordered::detail::true_type()); + } } template @@ -979,65 +1095,10 @@ unordered_set::unordered_set( unordered_set const& other, allocator_type const& a) : table_(other.table_, a) { -} - -template -template -unordered_set::unordered_set(InputIt f, InputIt l) - : table_(boost::unordered::detail::initial_size(f, l), hasher(), - key_equal(), allocator_type()) -{ - table_.insert_range(f, l); -} - -template -template -unordered_set::unordered_set( - InputIt f, InputIt l, size_type n, const hasher& hf, const key_equal& eql) - : table_(boost::unordered::detail::initial_size(f, l, n), hf, eql, - allocator_type()) -{ - table_.insert_range(f, l); -} - -template -template -unordered_set::unordered_set(InputIt f, InputIt l, size_type n, - const hasher& hf, const key_equal& eql, const allocator_type& a) - : table_(boost::unordered::detail::initial_size(f, l, n), hf, eql, a) -{ - table_.insert_range(f, l); -} - -template -template -unordered_set::unordered_set(InputIt f, InputIt l, size_type n, - const hasher& hf, const allocator_type& a) - : table_( - boost::unordered::detail::initial_size(f, l, n), hf, key_equal(), a) -{ - table_.insert_range(f, l); -} - -template -template -unordered_set::unordered_set( - InputIt f, InputIt l, size_type n, const allocator_type& a) - : table_(boost::unordered::detail::initial_size(f, l, n), hasher(), - key_equal(), a) -{ - table_.insert_range(f, l); -} - -template -unordered_set::~unordered_set() BOOST_NOEXCEPT -{ -} - -template -unordered_set::unordered_set(unordered_set const& other) - : table_(other.table_) -{ + if (other.table_.size_) { + table_.copy_buckets( + other.table_, boost::unordered::detail::true_type()); + } } template @@ -1045,6 +1106,12 @@ unordered_set::unordered_set( BOOST_RV_REF(unordered_set) other, allocator_type const& a) : table_(other.table_, a, boost::unordered::detail::move_tag()) { + if (table_.node_alloc() == other.table_.node_alloc()) { + table_.move_buckets_from(other.table_); + } else if (other.table_.size_) { + // TODO: Could pick new bucket size? + table_.move_buckets(other.table_); + } } #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) @@ -1057,7 +1124,54 @@ unordered_set::unordered_set(std::initializer_list list, boost::unordered::detail::initial_size(list.begin(), list.end(), n), hf, eql, a) { - table_.insert_range(list.begin(), list.end()); + this->insert(list.begin(), list.end()); +} + +#endif + +template +unordered_set::unordered_set(size_type n, const allocator_type& a) + : table_(n, hasher(), key_equal(), a) +{ +} + +template +unordered_set::unordered_set( + size_type n, const hasher& hf, const allocator_type& a) + : table_(n, hf, key_equal(), a) +{ +} + +template +template +unordered_set::unordered_set( + InputIt f, InputIt l, size_type n, const allocator_type& a) + : table_(boost::unordered::detail::initial_size(f, l, n), hasher(), + key_equal(), a) +{ + this->insert(f, l); +} + +template +template +unordered_set::unordered_set(InputIt f, InputIt l, size_type n, + const hasher& hf, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(f, l, n), hf, key_equal(), a) +{ + this->insert(f, l); +} + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + +template +unordered_set::unordered_set(std::initializer_list list, + size_type n, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(list.begin(), list.end(), n), + hasher(), key_equal(), a) +{ + this->insert(list.begin(), list.end()); } template @@ -1067,25 +1181,24 @@ unordered_set::unordered_set(std::initializer_list list, boost::unordered::detail::initial_size(list.begin(), list.end(), n), hf, key_equal(), a) { - table_.insert_range(list.begin(), list.end()); + this->insert(list.begin(), list.end()); } +#endif + template -unordered_set::unordered_set(std::initializer_list list, - size_type n, const allocator_type& a) - : table_( - boost::unordered::detail::initial_size(list.begin(), list.end(), n), - hasher(), key_equal(), a) +unordered_set::~unordered_set() BOOST_NOEXCEPT { - table_.insert_range(list.begin(), list.end()); } +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + template unordered_set& unordered_set::operator=( std::initializer_list list) { - table_.clear(); - table_.insert_range(list.begin(), list.end()); + this->clear(); + this->insert(list.begin(), list.end()); return *this; } @@ -1096,7 +1209,13 @@ unordered_set& unordered_set::operator=( template std::size_t unordered_set::max_size() const BOOST_NOEXCEPT { - return table_.max_size(); + using namespace std; + + // size < mlf_ * count + return boost::unordered::detail::double_to_size( + ceil(static_cast(table_.mlf_) * + static_cast(table_.max_bucket_count()))) - + 1; } // modifiers @@ -1105,14 +1224,17 @@ template template void unordered_set::insert(InputIt first, InputIt last) { - table_.insert_range(first, last); + if (first != last) { + table_.insert_range_unique( + table::extractor::extract(*first), first, last); + } } #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) template void unordered_set::insert(std::initializer_list list) { - table_.insert_range(list.begin(), list.end()); + this->insert(list.begin(), list.end()); } #endif @@ -1120,31 +1242,37 @@ template typename unordered_set::iterator unordered_set::erase( const_iterator position) { - return table_.erase(position); + node_pointer node = table::get_node(position); + BOOST_ASSERT(node); + node_pointer next = table::next_node(node); + table_.erase_nodes_unique(node, next); + return iterator(next); } template typename unordered_set::size_type unordered_set::erase( const key_type& k) { - return table_.erase_key(k); + return table_.erase_key_unique(k); } template typename unordered_set::iterator unordered_set::erase( const_iterator first, const_iterator last) { - return table_.erase_range(first, last); -} - -template -void unordered_set::clear() -{ - table_.clear(); + node_pointer last_node = table::get_node(last); + if (first == last) + return iterator(last_node); + table_.erase_nodes_unique(table::get_node(first), last_node); + return iterator(last_node); } template void unordered_set::swap(unordered_set& other) +// C++17 support: BOOST_NOEXCEPT_IF( +// value_allocator_traits::is_always_equal::value && +// is_nothrow_move_assignable_v && +// is_nothrow_move_assignable_v

) { table_.swap(other.table_); } @@ -1170,7 +1298,7 @@ template void unordered_set::merge( boost::unordered_set& source) { - table_.merge_impl(source.table_); + table_.merge_unique(source.table_); } #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) @@ -1179,17 +1307,16 @@ template void unordered_set::merge( boost::unordered_set&& source) { - table_.merge_impl(source.table_); + table_.merge_unique(source.table_); } #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template template void unordered_set::merge( boost::unordered_multiset& source) { - table_.merge_impl(source.table_); + table_.merge_unique(source.table_); } #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) @@ -1198,10 +1325,9 @@ template void unordered_set::merge( boost::unordered_multiset&& source) { - table_.merge_impl(source.table_); + table_.merge_unique(source.table_); } #endif -#endif // lookup @@ -1218,14 +1344,15 @@ typename unordered_set::const_iterator unordered_set::find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const { - return const_iterator(table_.generic_find_node(k, hash, eq)); + return const_iterator( + table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq)); } template typename unordered_set::size_type unordered_set::count( const key_type& k) const { - return table_.count(k); + return table_.find_node(k) ? 1 : 0; } template @@ -1233,7 +1360,9 @@ std::pair::const_iterator, typename unordered_set::const_iterator> unordered_set::equal_range(const key_type& k) const { - return table_.equal_range(k); + node_pointer n = table_.find_node(k); + return std::make_pair( + const_iterator(n), const_iterator(n ? table::next_node(n) : n)); } template @@ -1248,7 +1377,9 @@ unordered_set::bucket_size(size_type n) const template float unordered_set::load_factor() const BOOST_NOEXCEPT { - return table_.load_factor(); + BOOST_ASSERT(table_.bucket_count_ != 0); + return static_cast(table_.size_) / + static_cast(table_.bucket_count_); } template @@ -1266,7 +1397,8 @@ void unordered_set::rehash(size_type n) template void unordered_set::reserve(size_type n) { - table_.reserve(n); + table_.rehash(static_cast( + std::ceil(static_cast(n) / table_.mlf_))); } template @@ -1279,7 +1411,7 @@ inline bool operator==( unordered_set x; }; #endif - return m1.table_.equals(m2.table_); + return m1.table_.equals_unique(m2.table_); } template @@ -1292,11 +1424,12 @@ inline bool operator!=( unordered_set x; }; #endif - return !m1.table_.equals(m2.table_); + return !m1.table_.equals_unique(m2.table_); } template inline void swap(unordered_set& m1, unordered_set& m2) + BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2))) { #if BOOST_WORKAROUND(__CODEGEARC__, BOOST_TESTED_AT(0x0613)) struct dummy @@ -1324,17 +1457,26 @@ unordered_multiset::unordered_multiset(size_type n, } template -unordered_multiset::unordered_multiset( - size_type n, const allocator_type& a) - : table_(n, hasher(), key_equal(), a) +template +unordered_multiset::unordered_multiset(InputIt f, InputIt l, + size_type n, const hasher& hf, const key_equal& eql, + const allocator_type& a) + : table_(boost::unordered::detail::initial_size(f, l, n), hf, eql, a) { + this->insert(f, l); } template unordered_multiset::unordered_multiset( - size_type n, const hasher& hf, const allocator_type& a) - : table_(n, hf, key_equal(), a) + unordered_multiset const& other) + : table_(other.table_, + unordered_multiset::value_allocator_traits:: + select_on_container_copy_construction(other.get_allocator())) { + if (other.table_.size_) { + table_.copy_buckets( + other.table_, boost::unordered::detail::false_type()); + } } template @@ -1349,67 +1491,10 @@ unordered_multiset::unordered_multiset( unordered_multiset const& other, allocator_type const& a) : table_(other.table_, a) { -} - -template -template -unordered_multiset::unordered_multiset(InputIt f, InputIt l) - : table_(boost::unordered::detail::initial_size(f, l), hasher(), - key_equal(), allocator_type()) -{ - table_.insert_range(f, l); -} - -template -template -unordered_multiset::unordered_multiset( - InputIt f, InputIt l, size_type n, const hasher& hf, const key_equal& eql) - : table_(boost::unordered::detail::initial_size(f, l, n), hf, eql, - allocator_type()) -{ - table_.insert_range(f, l); -} - -template -template -unordered_multiset::unordered_multiset(InputIt f, InputIt l, - size_type n, const hasher& hf, const key_equal& eql, - const allocator_type& a) - : table_(boost::unordered::detail::initial_size(f, l, n), hf, eql, a) -{ - table_.insert_range(f, l); -} - -template -template -unordered_multiset::unordered_multiset(InputIt f, InputIt l, - size_type n, const hasher& hf, const allocator_type& a) - : table_( - boost::unordered::detail::initial_size(f, l, n), hf, key_equal(), a) -{ - table_.insert_range(f, l); -} - -template -template -unordered_multiset::unordered_multiset( - InputIt f, InputIt l, size_type n, const allocator_type& a) - : table_(boost::unordered::detail::initial_size(f, l, n), hasher(), - key_equal(), a) -{ - table_.insert_range(f, l); -} - -template -unordered_multiset::~unordered_multiset() BOOST_NOEXCEPT -{ -} - -template -unordered_multiset::unordered_multiset( - unordered_multiset const& other) - : table_(other.table_) -{ + if (other.table_.size_) { + table_.copy_buckets( + other.table_, boost::unordered::detail::false_type()); + } } template @@ -1417,6 +1502,12 @@ unordered_multiset::unordered_multiset( BOOST_RV_REF(unordered_multiset) other, allocator_type const& a) : table_(other.table_, a, boost::unordered::detail::move_tag()) { + if (table_.node_alloc() == other.table_.node_alloc()) { + table_.move_buckets_from(other.table_); + } else if (other.table_.size_) { + // TODO: Could pick new bucket size? + table_.move_buckets_equiv(other.table_); + } } #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) @@ -1429,7 +1520,56 @@ unordered_multiset::unordered_multiset( boost::unordered::detail::initial_size(list.begin(), list.end(), n), hf, eql, a) { - table_.insert_range(list.begin(), list.end()); + this->insert(list.begin(), list.end()); +} + +#endif + +template +unordered_multiset::unordered_multiset( + size_type n, const allocator_type& a) + : table_(n, hasher(), key_equal(), a) +{ +} + +template +unordered_multiset::unordered_multiset( + size_type n, const hasher& hf, const allocator_type& a) + : table_(n, hf, key_equal(), a) +{ +} + +template +template +unordered_multiset::unordered_multiset( + InputIt f, InputIt l, size_type n, const allocator_type& a) + : table_(boost::unordered::detail::initial_size(f, l, n), hasher(), + key_equal(), a) +{ + this->insert(f, l); +} + +template +template +unordered_multiset::unordered_multiset(InputIt f, InputIt l, + size_type n, const hasher& hf, const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(f, l, n), hf, key_equal(), a) +{ + this->insert(f, l); +} + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + +template +unordered_multiset::unordered_multiset( + std::initializer_list list, size_type n, + const allocator_type& a) + : table_( + boost::unordered::detail::initial_size(list.begin(), list.end(), n), + hasher(), key_equal(), a) +{ + this->insert(list.begin(), list.end()); } template @@ -1440,26 +1580,24 @@ unordered_multiset::unordered_multiset( boost::unordered::detail::initial_size(list.begin(), list.end(), n), hf, key_equal(), a) { - table_.insert_range(list.begin(), list.end()); + this->insert(list.begin(), list.end()); } +#endif + template -unordered_multiset::unordered_multiset( - std::initializer_list list, size_type n, - const allocator_type& a) - : table_( - boost::unordered::detail::initial_size(list.begin(), list.end(), n), - hasher(), key_equal(), a) +unordered_multiset::~unordered_multiset() BOOST_NOEXCEPT { - table_.insert_range(list.begin(), list.end()); } +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + template unordered_multiset& unordered_multiset::operator=( std::initializer_list list) { - table_.clear(); - table_.insert_range(list.begin(), list.end()); + this->clear(); + this->insert(list.begin(), list.end()); return *this; } @@ -1470,7 +1608,13 @@ unordered_multiset& unordered_multiset::operator=( template std::size_t unordered_multiset::max_size() const BOOST_NOEXCEPT { - return table_.max_size(); + using namespace std; + + // size < mlf_ * count + return boost::unordered::detail::double_to_size( + ceil(static_cast(table_.mlf_) * + static_cast(table_.max_bucket_count()))) - + 1; } // modifiers @@ -1479,7 +1623,7 @@ template template void unordered_multiset::insert(InputIt first, InputIt last) { - table_.insert_range(first, last); + table_.insert_range_equiv(first, last); } #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) @@ -1487,7 +1631,7 @@ template void unordered_multiset::insert( std::initializer_list list) { - table_.insert_range(list.begin(), list.end()); + this->insert(list.begin(), list.end()); } #endif @@ -1495,31 +1639,37 @@ template typename unordered_multiset::iterator unordered_multiset::erase(const_iterator position) { - return table_.erase(position); + node_pointer node = table::get_node(position); + BOOST_ASSERT(node); + node_pointer next = table::next_node(node); + table_.erase_nodes_equiv(node, next); + return iterator(next); } template typename unordered_multiset::size_type unordered_multiset::erase(const key_type& k) { - return table_.erase_key(k); + return table_.erase_key_equiv(k); } template typename unordered_multiset::iterator unordered_multiset::erase(const_iterator first, const_iterator last) { - return table_.erase_range(first, last); -} - -template -void unordered_multiset::clear() -{ - table_.clear(); + node_pointer last_node = table::get_node(last); + if (first == last) + return iterator(last_node); + table_.erase_nodes_equiv(table::get_node(first), last_node); + return iterator(last_node); } template void unordered_multiset::swap(unordered_multiset& other) +// C++17 support: BOOST_NOEXCEPT_IF( +// value_allocator_traits::is_always_equal::value && +// is_nothrow_move_assignable_v && +// is_nothrow_move_assignable_v

) { table_.swap(other.table_); } @@ -1562,7 +1712,6 @@ void unordered_multiset::merge( } #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template template void unordered_multiset::merge( @@ -1584,7 +1733,6 @@ void unordered_multiset::merge( } } #endif -#endif // lookup @@ -1601,14 +1749,16 @@ typename unordered_multiset::const_iterator unordered_multiset::find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const { - return const_iterator(table_.generic_find_node(k, hash, eq)); + return const_iterator( + table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq)); } template typename unordered_multiset::size_type unordered_multiset::count(const key_type& k) const { - return table_.count(k); + node_pointer n = table_.find_node(k); + return n ? table_.group_count(n) : 0; } template @@ -1616,7 +1766,9 @@ std::pair::const_iterator, typename unordered_multiset::const_iterator> unordered_multiset::equal_range(const key_type& k) const { - return table_.equal_range(k); + node_pointer n = table_.find_node(k); + return std::make_pair( + const_iterator(n), const_iterator(n ? table_.next_group(n) : n)); } template @@ -1631,7 +1783,9 @@ unordered_multiset::bucket_size(size_type n) const template float unordered_multiset::load_factor() const BOOST_NOEXCEPT { - return table_.load_factor(); + BOOST_ASSERT(table_.bucket_count_ != 0); + return static_cast(table_.size_) / + static_cast(table_.bucket_count_); } template @@ -1649,7 +1803,8 @@ void unordered_multiset::rehash(size_type n) template void unordered_multiset::reserve(size_type n) { - table_.reserve(n); + table_.rehash(static_cast( + std::ceil(static_cast(n) / table_.mlf_))); } template @@ -1662,7 +1817,7 @@ inline bool operator==(unordered_multiset const& m1, unordered_multiset x; }; #endif - return m1.table_.equals(m2.table_); + return m1.table_.equals_equiv(m2.table_); } template @@ -1675,12 +1830,13 @@ inline bool operator!=(unordered_multiset const& m1, unordered_multiset x; }; #endif - return !m1.table_.equals(m2.table_); + return !m1.table_.equals_equiv(m2.table_); } template inline void swap( unordered_multiset& m1, unordered_multiset& m2) + BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2))) { #if BOOST_WORKAROUND(__CODEGEARC__, BOOST_TESTED_AT(0x0613)) struct dummy @@ -1695,10 +1851,11 @@ template class node_handle_set { BOOST_MOVABLE_BUT_NOT_COPYABLE(node_handle_set) - template - friend struct ::boost::unordered::detail::table_impl; - template - friend struct ::boost::unordered::detail::grouped_table_impl; + template friend struct ::boost::unordered::detail::table; + template + friend class unordered_set; + template + friend class unordered_multiset; typedef typename boost::unordered::detail::rebind_wrap::type value_allocator; @@ -1720,13 +1877,7 @@ template class node_handle_set bool has_alloc_; boost::unordered::detail::value_base alloc_; - public: - BOOST_CONSTEXPR node_handle_set() BOOST_NOEXCEPT : ptr_(), has_alloc_(false) - { - } - - /*BOOST_CONSTEXPR */ node_handle_set( - node_pointer ptr, allocator_type const& a) + node_handle_set(node_pointer ptr, allocator_type const& a) : ptr_(ptr), has_alloc_(false) { if (ptr_) { @@ -1735,6 +1886,11 @@ template class node_handle_set } } + public: + BOOST_CONSTEXPR node_handle_set() BOOST_NOEXCEPT : ptr_(), has_alloc_(false) + { + } + ~node_handle_set() { if (has_alloc_ && ptr_) { diff --git a/include/boost/unordered/unordered_set_fwd.hpp b/include/boost/unordered/unordered_set_fwd.hpp index d3a3b51e..24842c80 100644 --- a/include/boost/unordered/unordered_set_fwd.hpp +++ b/include/boost/unordered/unordered_set_fwd.hpp @@ -29,7 +29,8 @@ template inline bool operator!=( unordered_set const&, unordered_set const&); template -inline void swap(unordered_set& m1, unordered_set& m2); +inline void swap(unordered_set& m1, unordered_set& m2) + BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2))); template , class P = std::equal_to, class A = std::allocator > @@ -43,7 +44,8 @@ inline bool operator!=(unordered_multiset const&, unordered_multiset const&); template inline void swap( - unordered_multiset& m1, unordered_multiset& m2); + unordered_multiset& m1, unordered_multiset& m2) + BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2))); template class node_handle_set; template struct insert_return_type_set; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 2aad7e8d..e06b569f 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 ; @@ -45,7 +46,7 @@ test-suite unordered [ run unordered/insert_hint_tests.cpp ] [ run unordered/emplace_tests.cpp ] [ run unordered/unnecessary_copy_tests.cpp ] - [ run unordered/erase_tests.cpp ] + [ run unordered/erase_tests.cpp : : : BOOST_UNORDERED_SUPPRESS_DEPRECATED ] [ run unordered/erase_equiv_tests.cpp ] [ run unordered/extract_tests.cpp ] [ run unordered/node_handle_tests.cpp ] @@ -61,6 +62,7 @@ test-suite unordered [ run unordered/rehash_tests.cpp ] [ run unordered/equality_tests.cpp ] [ run unordered/swap_tests.cpp ] + [ run unordered/detail_tests.cpp ] [ run unordered/compile_set.cpp : : : BOOST_UNORDERED_USE_MOVE @@ -90,4 +92,5 @@ test-suite unordered-exception [ run exception/rehash_exception_tests.cpp framework ] [ run exception/swap_exception_tests.cpp framework : : : BOOST_UNORDERED_SWAP_METHOD=2 ] + [ run exception/merge_exception_tests.cpp framework ] ; diff --git a/test/exception/assign_exception_tests.cpp b/test/exception/assign_exception_tests.cpp index 3295a4e8..cad7fb35 100644 --- a/test/exception/assign_exception_tests.cpp +++ b/test/exception/assign_exception_tests.cpp @@ -7,6 +7,7 @@ #include "../helpers/invariants.hpp" #include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" #if defined(BOOST_MSVC) #pragma warning(disable : 4512) // assignment operator could not be generated @@ -17,11 +18,22 @@ test::seed_t initialize_seed(12847); template struct self_assign_base : public test::exception_base { test::random_values values; - self_assign_base(std::size_t count = 0) : values(count) {} + self_assign_base(std::size_t count = 0) : values(count, test::limited_range) + { + } typedef T data_type; T init() const { return T(values.begin(), values.end()); } - void run(T& x) const { x = x; } + + void run(T& x) const + { + x = x; + + DISABLE_EXCEPTIONS; + test::check_container(x, values); + test::check_equivalent_keys(x); + } + void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const { test::check_equivalent_keys(x); @@ -57,7 +69,16 @@ template struct assign_base : public test::exception_base typedef T data_type; T init() const { return T(x); } - void run(T& x1) const { x1 = y; } + + void run(T& x1) const + { + x1 = y; + + DISABLE_EXCEPTIONS; + test::check_container(x1, y_values); + test::check_equivalent_keys(x1); + } + void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x1) const { test::check_equivalent_keys(x1); @@ -76,11 +97,12 @@ template struct assign_base : public test::exception_base template struct assign_values : assign_base { assign_values(unsigned int count1, unsigned int count2, int tag1, int tag2, - float mlf1 = 1.0, float mlf2 = 1.0) + test::random_generator gen = test::default_generator, float mlf1 = 1.0, + float mlf2 = 1.0) : assign_base(tag1, tag2, mlf1, mlf2) { - this->x_values.fill(count1); - this->y_values.fill(count2); + this->x_values.fill(count1, gen); + this->y_values.fill(count2, gen); this->x.insert(this->x_values.begin(), this->x_values.end()); this->y.insert(this->y_values.begin(), this->y_values.end()); } @@ -96,11 +118,21 @@ template struct assign_test2 : assign_values assign_test2() : assign_values(60, 0, 0, 0) {} }; +template struct assign_test2a : assign_values +{ + assign_test2a() : assign_values(60, 0, 0, 0, test::limited_range) {} +}; + template struct assign_test3 : assign_values { assign_test3() : assign_values(0, 60, 0, 0) {} }; +template struct assign_test3a : assign_values +{ + assign_test3a() : assign_values(0, 60, 0, 0, test::limited_range) {} +}; + template struct assign_test4 : assign_values { assign_test4() : assign_values(10, 10, 1, 2) {} @@ -111,9 +143,17 @@ template struct assign_test4a : assign_values assign_test4a() : assign_values(10, 100, 1, 2) {} }; +template struct assign_test4b : assign_values +{ + assign_test4b() : assign_values(10, 100, 1, 2, test::limited_range) {} +}; + template struct assign_test5 : assign_values { - assign_test5() : assign_values(5, 60, 0, 0, 1.0f, 0.1f) {} + assign_test5() + : assign_values(5, 60, 0, 0, test::default_generator, 1.0f, 0.1f) + { + } }; template struct equivalent_test1 : assign_base @@ -131,8 +171,15 @@ template struct equivalent_test1 : assign_base } }; -EXCEPTION_TESTS((self_assign_test1)(self_assign_test2)(assign_test1)( - assign_test2)(assign_test3)(assign_test4)(assign_test4a)( - assign_test5)(equivalent_test1), +// clang-format off +EXCEPTION_TESTS_REPEAT(5, + (self_assign_test1)(self_assign_test2) + (assign_test1)(assign_test2)(assign_test2a) + (assign_test3)(assign_test3a) + (assign_test4)(assign_test4a)(assign_test4b) + (assign_test5) + (equivalent_test1), CONTAINER_SEQ) +// clang-format on + RUN_TESTS() diff --git a/test/exception/constructor_exception_tests.cpp b/test/exception/constructor_exception_tests.cpp index db5023f7..39468054 100644 --- a/test/exception/constructor_exception_tests.cpp +++ b/test/exception/constructor_exception_tests.cpp @@ -6,7 +6,9 @@ #include "./containers.hpp" #include "../helpers/input_iterator.hpp" +#include "../helpers/invariants.hpp" #include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" template inline void avoid_unused_warning(T const&) {} @@ -25,7 +27,10 @@ template struct construct_test1 : public objects, test::exception_base void run() const { T x; - avoid_unused_warning(x); + + DISABLE_EXCEPTIONS; + BOOST_TEST(x.empty()); + test::check_equivalent_keys(x); } }; @@ -34,7 +39,10 @@ template struct construct_test2 : public objects, test::exception_base void run() const { T x(300); - avoid_unused_warning(x); + + DISABLE_EXCEPTIONS; + BOOST_TEST(x.empty()); + test::check_equivalent_keys(x); } }; @@ -43,7 +51,10 @@ template struct construct_test3 : public objects, test::exception_base void run() const { T x(0, hash); - avoid_unused_warning(x); + + DISABLE_EXCEPTIONS; + BOOST_TEST(x.empty()); + test::check_equivalent_keys(x); } }; @@ -52,7 +63,10 @@ template struct construct_test4 : public objects, test::exception_base void run() const { T x(0, hash, equal_to); - avoid_unused_warning(x); + + DISABLE_EXCEPTIONS; + BOOST_TEST(x.empty()); + test::check_equivalent_keys(x); } }; @@ -61,7 +75,10 @@ template struct construct_test5 : public objects, test::exception_base void run() const { T x(50, hash, equal_to, allocator); - avoid_unused_warning(x); + + DISABLE_EXCEPTIONS; + BOOST_TEST(x.empty()); + test::check_equivalent_keys(x); } }; @@ -70,7 +87,10 @@ template struct construct_test6 : public objects, test::exception_base void run() const { T x(allocator); - avoid_unused_warning(x); + + DISABLE_EXCEPTIONS; + BOOST_TEST(x.empty()); + test::check_equivalent_keys(x); } }; @@ -78,8 +98,8 @@ template struct range : public test::exception_base { test::random_values values; - range() : values(5) {} - range(unsigned int count) : values(count) {} + range() : values(5, test::limited_range) {} + range(unsigned int count) : values(count, test::limited_range) {} }; template struct range_construct_test1 : public range, objects @@ -87,7 +107,10 @@ template struct range_construct_test1 : public range, objects void run() const { T x(this->values.begin(), this->values.end()); - avoid_unused_warning(x); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); } }; @@ -96,7 +119,10 @@ template struct range_construct_test2 : public range, objects void run() const { T x(this->values.begin(), this->values.end(), 0); - avoid_unused_warning(x); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); } }; @@ -105,7 +131,10 @@ template struct range_construct_test3 : public range, objects void run() const { T x(this->values.begin(), this->values.end(), 0, hash); - avoid_unused_warning(x); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); } }; @@ -114,7 +143,10 @@ template struct range_construct_test4 : public range, objects void run() const { T x(this->values.begin(), this->values.end(), 100, hash, equal_to); - avoid_unused_warning(x); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); } }; @@ -128,7 +160,10 @@ template struct range_construct_test5 : public range, objects { T x(this->values.begin(), this->values.end(), 0, hash, equal_to, allocator); - avoid_unused_warning(x); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); } }; @@ -143,7 +178,10 @@ template struct input_range_construct_test : public range, objects end = this->values.end(); T x(test::input_iterator(begin), test::input_iterator(end), 0, hash, equal_to, allocator); - avoid_unused_warning(x); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); } }; @@ -156,7 +194,10 @@ template struct copy_range_construct_test : public range, objects T x(test::copy_iterator(this->values.begin()), test::copy_iterator(this->values.end()), 0, hash, equal_to, allocator); - avoid_unused_warning(x); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); } }; diff --git a/test/exception/copy_exception_tests.cpp b/test/exception/copy_exception_tests.cpp index 9bb87eca..ba6b2bcf 100644 --- a/test/exception/copy_exception_tests.cpp +++ b/test/exception/copy_exception_tests.cpp @@ -5,7 +5,9 @@ #include "./containers.hpp" +#include "../helpers/invariants.hpp" #include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" template inline void avoid_unused_warning(T const&) {} @@ -18,7 +20,10 @@ template struct copy_test1 : public test::exception_base void run() const { T y(x); - avoid_unused_warning(y); + + DISABLE_EXCEPTIONS; + BOOST_TEST(y.empty()); + test::check_equivalent_keys(y); } }; @@ -27,12 +32,18 @@ template struct copy_test2 : public test::exception_base test::random_values values; T x; - copy_test2() : values(5), x(values.begin(), values.end()) {} + copy_test2() + : values(5, test::limited_range), x(values.begin(), values.end()) + { + } void run() const { T y(x); - avoid_unused_warning(y); + + DISABLE_EXCEPTIONS; + test::check_container(y, this->values); + test::check_equivalent_keys(y); } }; @@ -46,7 +57,30 @@ template struct copy_test3 : public test::exception_base void run() const { T y(x); - avoid_unused_warning(y); + + DISABLE_EXCEPTIONS; + test::check_container(y, this->values); + test::check_equivalent_keys(y); + } +}; + +template struct copy_test3a : public test::exception_base +{ + test::random_values values; + T x; + + copy_test3a() + : values(100, test::limited_range), x(values.begin(), values.end()) + { + } + + void run() const + { + T y(x); + + DISABLE_EXCEPTIONS; + test::check_container(y, this->values); + test::check_equivalent_keys(y); } }; @@ -61,10 +95,17 @@ template struct copy_with_allocator_test : public test::exception_base void run() const { T y(x, allocator); - avoid_unused_warning(y); + + DISABLE_EXCEPTIONS; + test::check_container(y, this->values); + test::check_equivalent_keys(y); } }; -EXCEPTION_TESTS((copy_test1)(copy_test2)(copy_test3)(copy_with_allocator_test), +// clang-format off +EXCEPTION_TESTS( + (copy_test1)(copy_test2)(copy_test3)(copy_test3a)(copy_with_allocator_test), CONTAINER_SEQ) +// clang-format on + RUN_TESTS() diff --git a/test/exception/erase_exception_tests.cpp b/test/exception/erase_exception_tests.cpp index a939ded3..93e09439 100644 --- a/test/exception/erase_exception_tests.cpp +++ b/test/exception/erase_exception_tests.cpp @@ -14,7 +14,9 @@ test::seed_t initialize_seed(835193); template struct erase_test_base : public test::exception_base { test::random_values values; - erase_test_base(unsigned int count = 5) : values(count) {} + erase_test_base(unsigned int count = 5) : values(count, test::limited_range) + { + } typedef T data_type; @@ -43,6 +45,10 @@ template struct erase_by_key_test1 : public erase_test_base it != end; ++it) { x.erase(test::get_key(*it)); } + + DISABLE_EXCEPTIONS; + BOOST_TEST(x.empty()); + test::check_equivalent_keys(x); } }; diff --git a/test/exception/insert_exception_tests.cpp b/test/exception/insert_exception_tests.cpp index f8d635de..d5fc3f10 100644 --- a/test/exception/insert_exception_tests.cpp +++ b/test/exception/insert_exception_tests.cpp @@ -2,306 +2,414 @@ // Copyright 2006-2009 Daniel James. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - #include "./containers.hpp" #include "../helpers/helpers.hpp" #include "../helpers/invariants.hpp" #include "../helpers/random_values.hpp" #include "../helpers/strong.hpp" +#include "../helpers/tracker.hpp" #include #include test::seed_t initialize_seed(747373); -template struct insert_test_base : public test::exception_base +// Fill in a container so that it's about to rehash +template void rehash_prep(T& x) { - test::random_values values; - insert_test_base(unsigned int count = 5) - : values(count, test::limited_range) - { + using namespace std; + typedef BOOST_DEDUCED_TYPENAME T::size_type size_type; + + x.max_load_factor(0.25); + size_type bucket_count = x.bucket_count(); + size_type initial_elements = static_cast( + ceil((double)bucket_count * (double)x.max_load_factor()) - 1); + test::random_values v(initial_elements); + x.insert(v.begin(), v.end()); + BOOST_TEST(bucket_count == x.bucket_count()); +} + +// Overload to generate inserters that need type information. + +template +Inserter generate(Inserter inserter, T&) +{ + return inserter; +} + +// Get the iterator returned from an insert/emplace. + +template T get_iterator(T const& x) { return x; } + +template T get_iterator(std::pair const& x) +{ + return x.first; +} + +// Generic insert exception test for typical single element inserts.. + +template +void insert_exception_test_impl(T x, Inserter insert, Values const& v) +{ + test::strong strong; + + test::ordered tracker; + tracker.insert(x.begin(), x.end()); + + try { + ENABLE_EXCEPTIONS; + + for (typename Values::const_iterator it = v.begin(); it != v.end(); + ++it) { + strong.store(x, test::detail::tracker.count_allocations); + insert(x, it); + } + } catch (...) { + test::check_equivalent_keys(x); + insert.exception_check(x, strong); + throw; } - typedef T data_type; - typedef test::strong strong_type; + test::check_equivalent_keys(x); + insert.track(tracker, v.begin(), v.end()); + tracker.compare(x); +} - data_type init() const { return T(); } +// Simple insert exception test - void check BOOST_PREVENT_MACRO_SUBSTITUTION( - T const& x, strong_type const& strong) const +template +void insert_exception_test(T*, Inserter insert, test::random_generator gen) +{ + for (int i = 0; i < 5; ++i) { + test::random_values v(10, gen); + T x; + + EXCEPTION_LOOP(insert_exception_test_impl(x, generate(insert, x), v)); + } +} + +// Insert into a container which is about to hit its max load, so that it +// rehashes. + +template +void insert_rehash_exception_test( + T*, Inserter insert, test::random_generator gen) +{ + for (int i = 0; i < 5; ++i) { + T x; + rehash_prep(x); + + test::random_values v2(5, gen); + EXCEPTION_LOOP(insert_exception_test_impl(x, generate(insert, x), v2)); + } +} + +// Various methods for inserting a single element + +struct inserter_base +{ + template void exception_check(T& x, test::strong& strong) { std::string scope(test::scope); if (scope.find("hash::operator()") == std::string::npos) strong.test(x, test::detail::tracker.count_allocations); + } + + template + void track(T& tracker, Iterator begin, Iterator end) + { + tracker.insert(begin, end); + } +}; + +struct insert_lvalue_type : inserter_base +{ + template void operator()(T& x, Iterator it) + { + x.insert(*it); + } +} insert_lvalue; + +struct insert_lvalue_begin_type : inserter_base +{ + template void operator()(T& x, Iterator it) + { + x.insert(x.begin(), *it); + } +} insert_lvalue_begin; + +struct insert_lvalue_end_type : inserter_base +{ + template void operator()(T& x, Iterator it) + { + x.insert(x.end(), *it); + } +} insert_lvalue_end; + +struct insert_lvalue_pos_type +{ + template struct impl : inserter_base + { + typename T::iterator pos; + + impl(T& x) : pos(x.begin()) {} + + template void operator()(T& x, Iterator it) + { + pos = get_iterator(x.insert(pos, *it)); + } + }; + + template friend impl generate(insert_lvalue_pos_type, T& x) + { + return impl(x); + } +} insert_lvalue_pos; + +struct insert_single_item_range_type : inserter_base +{ + template void operator()(T& x, Iterator it) + { + x.insert(it, test::next(it)); + } +} insert_single_item_range; + +struct emplace_lvalue_type : inserter_base +{ + template void operator()(T& x, Iterator it) + { + x.emplace(*it); + } +} emplace_lvalue; + +struct emplace_lvalue_begin_type : inserter_base +{ + template void operator()(T& x, Iterator it) + { + x.emplace_hint(x.begin(), *it); + } +} emplace_lvalue_begin; + +struct emplace_lvalue_end_type : inserter_base +{ + template void operator()(T& x, Iterator it) + { + x.emplace_hint(x.end(), *it); + } +} emplace_lvalue_end; + +struct emplace_lvalue_pos_type +{ + template struct impl : inserter_base + { + typename T::iterator pos; + + impl(T& x) : pos(x.begin()) {} + + template void operator()(T& x, Iterator it) + { + pos = get_iterator(x.emplace_hint(pos, *it)); + } + }; + + template friend impl generate(emplace_lvalue_pos_type, T& x) + { + return impl(x); + } +} emplace_lvalue_pos; + +// Run the exception tests in various combinations. + +test_set* test_set_; +test_multiset* test_multiset_; +test_map* test_map_; +test_multimap* test_multimap_; + +using test::default_generator; +using test::limited_range; +using test::generate_collisions; + +// clang-format off +UNORDERED_TEST(insert_exception_test, + ((test_set_)(test_multiset_)(test_map_)(test_multimap_)) + ((insert_lvalue)(insert_lvalue_begin)(insert_lvalue_end) + (insert_lvalue_pos)(insert_single_item_range) + (emplace_lvalue)(emplace_lvalue_begin)(emplace_lvalue_end) + (emplace_lvalue_pos) + ) + ((default_generator)(limited_range)(generate_collisions)) +) + +UNORDERED_TEST(insert_rehash_exception_test, + ((test_set_)(test_multiset_)(test_map_)(test_multimap_)) + ((insert_lvalue)(insert_lvalue_begin)(insert_lvalue_end) + (insert_lvalue_pos)(insert_single_item_range) + (emplace_lvalue)(emplace_lvalue_begin)(emplace_lvalue_end) + (emplace_lvalue_pos) + ) + ((default_generator)(limited_range)(generate_collisions)) +) +// clang-format on + +// Repeat insert tests with pairs + +struct pair_emplace_type : inserter_base +{ + template void operator()(T& x, Iterator it) + { + x.emplace(boost::unordered::piecewise_construct, + boost::make_tuple(it->first), boost::make_tuple(it->second)); + } +} pair_emplace; + +struct pair_emplace2_type : inserter_base +{ + template void operator()(T& x, Iterator it) + { + x.emplace_hint(x.begin(), boost::unordered::piecewise_construct, + boost::make_tuple(it->first), + boost::make_tuple(it->second.tag1_, it->second.tag2_)); + } +} pair_emplace2; + +test_pair_set* test_pair_set_; +test_pair_multiset* test_pair_multiset_; + +// clang-format off +UNORDERED_TEST(insert_exception_test, + ((test_pair_set_)(test_pair_multiset_)(test_map_)(test_multimap_)) + ((pair_emplace)(pair_emplace2)) + ((default_generator)(limited_range)(generate_collisions)) +) +UNORDERED_TEST(insert_rehash_exception_test, + ((test_pair_set_)(test_pair_multiset_)(test_map_)(test_multimap_)) + ((pair_emplace)(pair_emplace2)) + ((default_generator)(limited_range)(generate_collisions)) +) +// clang-format on + +// Test inserting using operator[] + +struct try_emplace_type : inserter_base +{ + template void operator()(T& x, Iterator it) + { + x.try_emplace(it->first, it->second); + } +} try_emplace; + +struct try_emplace2_type : inserter_base +{ + template void operator()(T& x, Iterator it) + { + x.try_emplace(it->first, it->second.tag1_, it->second.tag2_); + } +} try_emplace2; + +struct map_inserter_base +{ + template void exception_check(T& x, test::strong& strong) + { + std::string scope(test::scope); + + if (scope.find("hash::operator()") == std::string::npos && + scope.find("::operator=") == std::string::npos) + strong.test(x, test::detail::tracker.count_allocations); + } + + template + void track(T& tracker, Iterator begin, Iterator end) + { + for (; begin != end; ++begin) { + tracker[begin->first] = begin->second; + } + } +}; + +struct map_insert_operator_type : map_inserter_base +{ + template void operator()(T& x, Iterator it) + { + x[it->first] = it->second; + } +} map_insert_operator; + +struct map_insert_or_assign_type : map_inserter_base +{ + template void operator()(T& x, Iterator it) + { + x.insert_or_assign(it->first, it->second); + } +} map_insert_or_assign; + +// clang-format off +UNORDERED_TEST(insert_exception_test, + ((test_map_)) + ((try_emplace)(try_emplace2)(map_insert_operator)(map_insert_or_assign)) + ((default_generator)(limited_range)(generate_collisions)) +) +UNORDERED_TEST(insert_rehash_exception_test, + ((test_map_)) + ((try_emplace)(try_emplace2)(map_insert_operator)(map_insert_or_assign)) + ((default_generator)(limited_range)(generate_collisions)) +) +// clang-format on + +// Range insert tests + +template +void insert_range_exception_test_impl(T x, Values const& v) +{ + test::ordered tracker; + tracker.insert(x.begin(), x.end()); + + try { + ENABLE_EXCEPTIONS; + x.insert(v.begin(), v.end()); + } catch (...) { test::check_equivalent_keys(x); + throw; } -}; -#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && \ - !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + test::check_equivalent_keys(x); + tracker.insert(v.begin(), v.end()); + tracker.compare(x); +} -template struct emplace_test1 : public insert_test_base +template +void insert_range_exception_test(T*, test::random_generator gen) { - typedef BOOST_DEDUCED_TYPENAME insert_test_base::strong_type strong_type; - - void run(T& x, strong_type& strong) const - { - for (BOOST_DEDUCED_TYPENAME test::random_values::const_iterator - it = this->values.begin(), - end = this->values.end(); - it != end; ++it) { - strong.store(x, test::detail::tracker.count_allocations); - x.emplace(*it); - } - } -}; - -#endif - -template struct insert_test1 : public insert_test_base -{ - typedef BOOST_DEDUCED_TYPENAME insert_test_base::strong_type strong_type; - - void run(T& x, strong_type& strong) const - { - for (BOOST_DEDUCED_TYPENAME test::random_values::const_iterator - it = this->values.begin(), - end = this->values.end(); - it != end; ++it) { - strong.store(x, test::detail::tracker.count_allocations); - x.insert(*it); - } - } -}; - -template struct insert_test2 : public insert_test_base -{ - typedef BOOST_DEDUCED_TYPENAME insert_test_base::strong_type strong_type; - - void run(T& x, strong_type& strong) const - { - for (BOOST_DEDUCED_TYPENAME test::random_values::const_iterator - it = this->values.begin(), - end = this->values.end(); - it != end; ++it) { - strong.store(x, test::detail::tracker.count_allocations); - x.insert(x.begin(), *it); - } - } -}; - -template struct insert_test3 : public insert_test_base -{ - void run(T& x) const { x.insert(this->values.begin(), this->values.end()); } - - void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const - { - test::check_equivalent_keys(x); - } -}; - -template struct insert_test4 : public insert_test_base -{ - typedef BOOST_DEDUCED_TYPENAME insert_test_base::strong_type strong_type; - - void run(T& x, strong_type& strong) const - { - for (BOOST_DEDUCED_TYPENAME test::random_values::const_iterator - it = this->values.begin(), - end = this->values.end(); - it != end; ++it) { - strong.store(x, test::detail::tracker.count_allocations); - x.insert(it, test::next(it)); - } - } -}; - -template struct insert_test_rehash1 : public insert_test_base -{ - typedef BOOST_DEDUCED_TYPENAME insert_test_base::strong_type strong_type; - - insert_test_rehash1() : insert_test_base(1000) {} - - T init() const - { - using namespace std; - typedef BOOST_DEDUCED_TYPENAME T::size_type size_type; - + for (int i = 0; i < 5; ++i) { + test::random_values v(10, gen); T x; - x.max_load_factor(0.25); - // TODO: This doesn't really work is bucket_count is 0 - size_type bucket_count = x.bucket_count(); - size_type initial_elements = static_cast( - ceil((double)bucket_count * (double)x.max_load_factor()) - 1); - BOOST_TEST(initial_elements < this->values.size()); - x.insert(this->values.begin(), - test::next(this->values.begin(), initial_elements)); - BOOST_TEST(bucket_count == x.bucket_count()); - return x; + + EXCEPTION_LOOP(insert_range_exception_test_impl(x, v)); } +} - void run(T& x, strong_type& strong) const - { - BOOST_DEDUCED_TYPENAME T::size_type bucket_count = x.bucket_count(); - int count = 0; - BOOST_DEDUCED_TYPENAME T::const_iterator pos = x.cbegin(); - - for (BOOST_DEDUCED_TYPENAME test::random_values::const_iterator - it = test::next(this->values.begin(), x.size()), - end = this->values.end(); - it != end && count < 10; ++it, ++count) { - strong.store(x, test::detail::tracker.count_allocations); - pos = x.insert(pos, *it); - } - - // This isn't actually a failure, but it means the test isn't doing its - // job. - BOOST_TEST(x.bucket_count() != bucket_count); - } -}; - -template struct insert_test_rehash2 : public insert_test_rehash1 +template +void insert_range_rehash_exception_test(T*, test::random_generator gen) { - typedef BOOST_DEDUCED_TYPENAME insert_test_base::strong_type strong_type; - - void run(T& x, strong_type& strong) const - { - BOOST_DEDUCED_TYPENAME T::size_type bucket_count = x.bucket_count(); - int count = 0; - - for (BOOST_DEDUCED_TYPENAME test::random_values::const_iterator - it = test::next(this->values.begin(), x.size()), - end = this->values.end(); - it != end && count < 10; ++it, ++count) { - strong.store(x, test::detail::tracker.count_allocations); - x.insert(*it); - } - - // This isn't actually a failure, but it means the test isn't doing its - // job. - BOOST_TEST(x.bucket_count() != bucket_count); - } -}; - -template struct insert_test_rehash3 : public insert_test_base -{ - BOOST_DEDUCED_TYPENAME T::size_type mutable rehash_bucket_count, - original_bucket_count; - - insert_test_rehash3() : insert_test_base(1000) {} - - T init() const - { - using namespace std; - typedef BOOST_DEDUCED_TYPENAME T::size_type size_type; - + for (int i = 0; i < 5; ++i) { T x; - x.max_load_factor(0.25); + rehash_prep(x); - original_bucket_count = x.bucket_count(); - rehash_bucket_count = - static_cast(ceil( - (double)original_bucket_count * (double)x.max_load_factor())) - - 1; - - size_type initial_elements = - rehash_bucket_count > 5 ? rehash_bucket_count - 5 : 1; - - BOOST_TEST(initial_elements < this->values.size()); - x.insert(this->values.begin(), - test::next(this->values.begin(), initial_elements)); - BOOST_TEST(original_bucket_count == x.bucket_count()); - return x; + test::random_values v2(5, gen); + EXCEPTION_LOOP(insert_range_exception_test_impl(x, v2)); } +} - void run(T& x) const - { - BOOST_DEDUCED_TYPENAME T::size_type bucket_count = x.bucket_count(); +// clang-format off +UNORDERED_TEST(insert_range_exception_test, + ((test_set_)(test_multiset_)(test_map_)(test_multimap_)) + ((default_generator)(limited_range)(generate_collisions)) +) - x.insert(test::next(this->values.begin(), x.size()), - test::next(this->values.begin(), x.size() + 20)); - - // This isn't actually a failure, but it means the test isn't doing its - // job. - BOOST_TEST(x.bucket_count() != bucket_count); - } - - void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const - { - if (x.size() < rehash_bucket_count) { - // BOOST_TEST(x.bucket_count() == original_bucket_count); - } - test::check_equivalent_keys(x); - } -}; - -#define BASIC_TESTS \ - (insert_test1)(insert_test2)(insert_test3)(insert_test4)( \ - insert_test_rehash1)(insert_test_rehash2)(insert_test_rehash3) - -#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && \ - !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) -#define ALL_TESTS (emplace_test1) BASIC_TESTS -#else -#define ALL_TESTS BASIC_TESTS -#endif - -EXCEPTION_TESTS(ALL_TESTS, CONTAINER_SEQ) - -template struct pair_emplace_test1 : public insert_test_base -{ - typedef BOOST_DEDUCED_TYPENAME insert_test_base::strong_type strong_type; - - void run(T& x, strong_type& strong) const - { - for (BOOST_DEDUCED_TYPENAME test::random_values::const_iterator - it = this->values.begin(), - end = this->values.end(); - it != end; ++it) { - strong.store(x, test::detail::tracker.count_allocations); - x.emplace(boost::unordered::piecewise_construct, - boost::make_tuple(it->first), boost::make_tuple(it->second)); - } - } -}; - -template struct pair_emplace_test2 : public insert_test_base -{ - typedef BOOST_DEDUCED_TYPENAME insert_test_base::strong_type strong_type; - - void run(T& x, strong_type& strong) const - { - for (BOOST_DEDUCED_TYPENAME test::random_values::const_iterator - it = this->values.begin(), - end = this->values.end(); - it != end; ++it) { - strong.store(x, test::detail::tracker.count_allocations); - x.emplace(boost::unordered::piecewise_construct, - boost::make_tuple(it->first), - boost::make_tuple(it->second.tag1_, it->second.tag2_)); - } - } -}; - -EXCEPTION_TESTS((pair_emplace_test1)(pair_emplace_test2), CONTAINER_PAIR_SEQ) - -template struct index_insert_test1 : public insert_test_base -{ - typedef BOOST_DEDUCED_TYPENAME insert_test_base::strong_type strong_type; - - void run(T& x, strong_type& strong) const - { - for (BOOST_DEDUCED_TYPENAME test::random_values::const_iterator - it = this->values.begin(), - end = this->values.end(); - it != end; ++it) { - strong.store(x, test::detail::tracker.count_allocations); - x[it->first]; - } - } -}; - -EXCEPTION_TESTS((index_insert_test1), (test_map)) +UNORDERED_TEST(insert_range_rehash_exception_test, + ((test_set_)(test_multiset_)(test_map_)(test_multimap_)) + ((default_generator)(limited_range)(generate_collisions)) +) +// clang-format on RUN_TESTS() diff --git a/test/exception/merge_exception_tests.cpp b/test/exception/merge_exception_tests.cpp new file mode 100644 index 00000000..48f6b697 --- /dev/null +++ b/test/exception/merge_exception_tests.cpp @@ -0,0 +1,103 @@ +#include "../helpers/exception_test.hpp" +#include "../helpers/invariants.hpp" +#include "../helpers/metafunctions.hpp" +#include "../helpers/random_values.hpp" +#include "./containers.hpp" + +template void merge_exception_test(T1 x, T2 y) +{ + std::size_t size = x.size() + y.size(); + + try { + ENABLE_EXCEPTIONS; + x.merge(y); + } catch (...) { + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); + throw; + } + + // Not a full check, just want to make sure the merge completed. + BOOST_TEST(size == x.size() + y.size()); + if (y.size()) { + BOOST_TEST(test::has_unique_keys::value); + for (typename T2::iterator it = y.begin(); it != y.end(); ++it) { + BOOST_TEST(x.find(test::get_key(*it)) != x.end()); + } + } + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); +} + +template +void merge_exception_test(T1 const*, T2 const*, std::size_t count12, int tag12, + test::random_generator gen1, test::random_generator gen2) +{ + std::size_t count1 = count12 / 256; + std::size_t count2 = count12 % 256; + int tag1 = tag12 / 256; + int tag2 = tag12 % 256; + test::random_values v1(count1, gen1); + test::random_values v2(count2, gen2); + T1 x(v1.begin(), v1.end(), 0, test::exception::hash(tag1), + test::exception::equal_to(tag1)); + T2 y(v2.begin(), v2.end(), 0, test::exception::hash(tag2), + test::exception::equal_to(tag2)); + + EXCEPTION_LOOP(merge_exception_test(x, y)) +} + +boost::unordered_set >* test_set_; +boost::unordered_multiset >* test_multiset_; +boost::unordered_map >* test_map_; +boost::unordered_multimap >* test_multimap_; + +using test::default_generator; +using test::generate_collisions; +using test::limited_range; + +// clang-format off +UNORDERED_MULTI_TEST(set_merge, merge_exception_test, + ((test_set_)(test_multiset_)) + ((test_set_)(test_multiset_)) + ((0x0000)(0x6400)(0x0064)(0x0a64)(0x3232)) + ((0x0000)(0x0001)(0x0102)) + ((default_generator)(limited_range)) + ((default_generator)(limited_range)) +) +UNORDERED_MULTI_TEST(map_merge, merge_exception_test, + ((test_map_)(test_multimap_)) + ((test_map_)(test_multimap_)) + ((0x0000)(0x6400)(0x0064)(0x0a64)(0x3232)) + ((0x0101)(0x0200)(0x0201)) + ((default_generator)(limited_range)) + ((default_generator)(limited_range)) +) +// Run fewer generate_collisions tests, as they're slow. +UNORDERED_MULTI_TEST(set_merge_collisions, merge_exception_test, + ((test_set_)(test_multiset_)) + ((test_set_)(test_multiset_)) + ((0x0a0a)) + ((0x0202)(0x0100)(0x0201)) + ((generate_collisions)) + ((generate_collisions)) +) +UNORDERED_MULTI_TEST(map_merge_collisions, merge_exception_test, + ((test_map_)(test_multimap_)) + ((test_map_)(test_multimap_)) + ((0x0a0a)) + ((0x0000)(0x0002)(0x0102)) + ((generate_collisions)) + ((generate_collisions)) +) +// clang-format on + +RUN_TESTS_QUIET() diff --git a/test/exception/move_assign_exception_tests.cpp b/test/exception/move_assign_exception_tests.cpp index 9a8d122b..5fe909a2 100644 --- a/test/exception/move_assign_exception_tests.cpp +++ b/test/exception/move_assign_exception_tests.cpp @@ -7,7 +7,7 @@ #include "../helpers/invariants.hpp" #include "../helpers/random_values.hpp" -#include +#include "../helpers/tracker.hpp" #if defined(BOOST_MSVC) #pragma warning( \ @@ -42,7 +42,12 @@ template struct move_assign_base : public test::exception_base T y1 = y; disable_exceptions.release(); x1 = boost::move(y1); + + DISABLE_EXCEPTIONS; + test::check_container(x1, y_values); + test::check_equivalent_keys(x1); } + void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x1) const { test::check_equivalent_keys(x1); @@ -64,8 +69,8 @@ template struct move_assign_values : move_assign_base int tag2, float mlf1 = 1.0, float mlf2 = 1.0) : move_assign_base(tag1, tag2, mlf1, mlf2) { - this->x_values.fill(count1); - this->y_values.fill(count2); + this->x_values.fill(count1, test::limited_range); + this->y_values.fill(count2, test::limited_range); this->x.insert(this->x_values.begin(), this->x_values.end()); this->y.insert(this->y_values.begin(), this->y_values.end()); } @@ -105,10 +110,10 @@ template struct equivalent_test1 : move_assign_base { equivalent_test1() : move_assign_base(0, 0) { - test::random_values x_values2(10); + test::random_values x_values2(10, test::limited_range); this->x_values.insert(x_values2.begin(), x_values2.end()); this->x_values.insert(x_values2.begin(), x_values2.end()); - test::random_values y_values2(10); + test::random_values y_values2(10, test::limited_range); this->y_values.insert(y_values2.begin(), y_values2.end()); this->y_values.insert(y_values2.begin(), y_values2.end()); this->x.insert(this->x_values.begin(), this->x_values.end()); diff --git a/test/exception/rehash_exception_tests.cpp b/test/exception/rehash_exception_tests.cpp index a65ebae9..f1386532 100644 --- a/test/exception/rehash_exception_tests.cpp +++ b/test/exception/rehash_exception_tests.cpp @@ -8,10 +8,9 @@ #include "../helpers/invariants.hpp" #include "../helpers/random_values.hpp" #include "../helpers/strong.hpp" +#include "../helpers/tracker.hpp" #include -#include - test::seed_t initialize_seed(3298597); template struct rehash_test_base : public test::exception_base @@ -19,7 +18,7 @@ template struct rehash_test_base : public test::exception_base test::random_values values; unsigned int n; rehash_test_base(unsigned int count = 100, unsigned int n_ = 0) - : values(count), n(n_) + : values(count, test::limited_range), n(n_) { } @@ -49,34 +48,82 @@ template struct rehash_test_base : public test::exception_base template struct rehash_test0 : rehash_test_base { rehash_test0() : rehash_test_base(0) {} - void run(T& x) const { x.rehash(0); } + void run(T& x) const + { + x.rehash(0); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); + } }; template struct rehash_test1 : rehash_test_base { rehash_test1() : rehash_test_base(0) {} - void run(T& x) const { x.rehash(200); } + void run(T& x) const + { + x.rehash(200); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); + } }; template struct rehash_test2 : rehash_test_base { rehash_test2() : rehash_test_base(0, 200) {} - void run(T& x) const { x.rehash(0); } + void run(T& x) const + { + x.rehash(0); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); + } }; template struct rehash_test3 : rehash_test_base { rehash_test3() : rehash_test_base(10, 0) {} - void run(T& x) const { x.rehash(200); } + void run(T& x) const + { + x.rehash(200); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); + } }; template struct rehash_test4 : rehash_test_base { rehash_test4() : rehash_test_base(10, 200) {} - void run(T& x) const { x.rehash(0); } + void run(T& x) const + { + x.rehash(0); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); + } }; -EXCEPTION_TESTS( - (rehash_test0)(rehash_test1)(rehash_test2)(rehash_test3)(rehash_test4), +template struct rehash_test5 : rehash_test_base +{ + rehash_test5() : rehash_test_base(200, 10) {} + void run(T& x) const + { + x.rehash(0); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); + } +}; + +EXCEPTION_TESTS((rehash_test0)(rehash_test1)(rehash_test2)(rehash_test3)( + rehash_test4)(rehash_test5), CONTAINER_SEQ) RUN_TESTS() diff --git a/test/exception/swap_exception_tests.cpp b/test/exception/swap_exception_tests.cpp index 12ba6403..d8b81128 100644 --- a/test/exception/swap_exception_tests.cpp +++ b/test/exception/swap_exception_tests.cpp @@ -7,6 +7,7 @@ #include "../helpers/invariants.hpp" #include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" #if defined(BOOST_MSVC) #pragma warning(disable : 4512) // assignment operator could not be generated @@ -17,11 +18,22 @@ test::seed_t initialize_seed(9387); template struct self_swap_base : public test::exception_base { test::random_values values; - self_swap_base(std::size_t count = 0) : values(count) {} + self_swap_base(std::size_t count = 0) : values(count, test::limited_range) + { + } typedef T data_type; T init() const { return T(values.begin(), values.end()); } - void run(T& x) const { x.swap(x); } + + void run(T& x) const + { + x.swap(x); + + DISABLE_EXCEPTIONS; + test::check_container(x, this->values); + test::check_equivalent_keys(x); + } + void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const { std::string scope(test::scope); @@ -55,7 +67,8 @@ template struct swap_base : public test::exception_base typedef BOOST_DEDUCED_TYPENAME T::allocator_type allocator_type; swap_base(unsigned int count1, unsigned int count2, int tag1, int tag2) - : x_values(count1), y_values(count2), + : x_values(count1, test::limited_range), + y_values(count2, test::limited_range), initial_x(x_values.begin(), x_values.end(), 0, hasher(tag1), key_equal(tag1), allocator_type(tag1)), initial_y( @@ -82,7 +95,14 @@ template struct swap_base : public test::exception_base d.x.swap(d.y); } catch (std::runtime_error) { } + + DISABLE_EXCEPTIONS; + test::check_container(d.x, this->y_values); + test::check_equivalent_keys(d.x); + test::check_container(d.y, this->x_values); + test::check_equivalent_keys(d.y); } + void check BOOST_PREVENT_MACRO_SUBSTITUTION(data_type const& d) const { std::string scope(test::scope); diff --git a/test/helpers/count.hpp b/test/helpers/count.hpp index c432fd1f..76248617 100644 --- a/test/helpers/count.hpp +++ b/test/helpers/count.hpp @@ -7,7 +7,6 @@ #define BOOST_UNORDERED_TEST_HELPERS_COUNT_HEAD #include -#include namespace test { struct object_count diff --git a/test/helpers/exception_test.hpp b/test/helpers/exception_test.hpp index 3bc0f734..7edf8848 100644 --- a/test/helpers/exception_test.hpp +++ b/test/helpers/exception_test.hpp @@ -241,8 +241,10 @@ template void exception_safety(Test const& f, char const* /*name*/) iteration = 0; bool success = false; + unsigned int failure_count = 0; char const* error_msg = 0; do { + int error_count = boost::detail::test_errors(); ++iteration; count = 0; @@ -252,19 +254,98 @@ template void exception_safety(Test const& f, char const* /*name*/) } catch (test_failure) { error_msg = "test_failure caught."; break; - } catch (test_exception) { - continue; + } catch (test_exception e) { + if (error_count != boost::detail::test_errors()) { + BOOST_LIGHTWEIGHT_TEST_OSTREAM + << "Iteration: " << iteration + << " Error found for epoint: " << e.name << std::endl; + } } catch (...) { error_msg = "Unexpected exception."; break; } - } while (!success); + + if (error_count != boost::detail::test_errors()) { + ++failure_count; + } + } while (!success && failure_count < 5); if (error_msg) { BOOST_ERROR(error_msg); } runner.end(); } + +// +// An alternative way to run exception tests. +// See merge_exception_tests.cpp for an example. + +struct exception_looper +{ + bool success; + unsigned int failure_count; + char const* error_msg; + int error_count; + + exception_looper() : success(false), failure_count(0), error_msg(0) {} + + void start() { iteration = 0; } + + bool loop_condition() const + { + return !error_msg && !success && failure_count < 5; + } + + void start_iteration() + { + error_count = boost::detail::test_errors(); + ++iteration; + count = 0; + } + + void successful_run() { success = true; } + + void test_failure_caught(test_failure const&) + { + error_msg = "test_failure caught."; + } + + void test_exception_caught(test_exception const& e) + { + if (error_count != boost::detail::test_errors()) { + BOOST_LIGHTWEIGHT_TEST_OSTREAM + << "Iteration: " << iteration + << " Error found for epoint: " << e.name << std::endl; + } + } + + void unexpected_exception_caught() { error_msg = "Unexpected exception."; } + + void end() + { + if (error_msg) { + BOOST_ERROR(error_msg); + } + } +}; + +#define EXCEPTION_LOOP(op) \ + test::lightweight::exception_looper looper; \ + looper.start(); \ + while (looper.loop_condition()) { \ + looper.start_iteration(); \ + try { \ + op; \ + looper.successful_run(); \ + } catch (test::lightweight::test_failure e) { \ + looper.test_failure_caught(e); \ + } catch (test::lightweight::test_exception e) { \ + looper.test_exception_caught(e); \ + } catch (...) { \ + looper.unexpected_exception_caught(); \ + } \ + } \ + looper.end(); } } diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index ffc5de4e..06091b94 100644 --- a/test/helpers/invariants.hpp +++ b/test/helpers/invariants.hpp @@ -63,20 +63,28 @@ template void check_equivalent_keys(X const& x1) BOOST_DEDUCED_TYPENAME X::size_type bucket = x1.bucket(key); BOOST_DEDUCED_TYPENAME X::const_local_iterator lit = x1.begin(bucket), lend = x1.end(bucket); - for (; lit != lend && !eq(get_key(*lit), key); ++lit) - continue; - if (lit == lend) + + unsigned int count_checked = 0; + for (; lit != lend && !eq(get_key(*lit), key); ++lit) { + ++count_checked; + } + + if (lit == lend) { BOOST_ERROR("Unable to find element with a local_iterator"); - unsigned int count2 = 0; - for (; lit != lend && eq(get_key(*lit), key); ++lit) - ++count2; - if (count != count2) - BOOST_ERROR("Element count doesn't match local_iterator."); - for (; lit != lend; ++lit) { - if (eq(get_key(*lit), key)) { - BOOST_ERROR("Non-adjacent element with equivalent key " - "in bucket."); - break; + std::cerr << "Checked: " << count_checked << " elements" + << std::endl; + } else { + unsigned int count2 = 0; + for (; lit != lend && eq(get_key(*lit), key); ++lit) + ++count2; + if (count != count2) + BOOST_ERROR("Element count doesn't match local_iterator."); + for (; lit != lend; ++lit) { + if (eq(get_key(*lit), key)) { + BOOST_ERROR("Non-adjacent element with equivalent key " + "in bucket."); + break; + } } } }; 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/helpers/test.hpp b/test/helpers/test.hpp index 393658ea..eaaa83f1 100644 --- a/test/helpers/test.hpp +++ b/test/helpers/test.hpp @@ -9,7 +9,6 @@ #include #include #include -#include #define UNORDERED_AUTO_TEST(x) \ struct BOOST_PP_CAT(x, _type) : public ::test::registered_test_base \ @@ -17,7 +16,7 @@ BOOST_PP_CAT(x, _type) \ () : ::test::registered_test_base(BOOST_PP_STRINGIZE(x)) \ { \ - ::test::test_list::add_test(this); \ + ::test::get_state().add_test(this); \ } \ void run(); \ }; \ @@ -27,12 +26,27 @@ #define RUN_TESTS() \ int main(int, char**) \ { \ - ::test::write_compiler_info(); \ - ::test::test_list::run_tests(); \ + BOOST_UNORDERED_TEST_COMPILER_INFO() \ + ::test::get_state().run_tests(); \ return boost::report_errors(); \ } +#define RUN_TESTS_QUIET() \ + int main(int, char**) \ + { \ + BOOST_UNORDERED_TEST_COMPILER_INFO() \ + ::test::get_state().run_tests(true); \ + return boost::report_errors(); \ + } + +#define UNORDERED_SUB_TEST(x) \ + for (int UNORDERED_SUB_TEST_VALUE = ::test::get_state().start_sub_test(x); \ + UNORDERED_SUB_TEST_VALUE; \ + UNORDERED_SUB_TEST_VALUE = \ + ::test::get_state().end_sub_test(x, UNORDERED_SUB_TEST_VALUE)) + namespace test { + struct registered_test_base { registered_test_base* next; @@ -42,56 +56,95 @@ struct registered_test_base virtual ~registered_test_base() {} }; -namespace test_list { -static inline registered_test_base*& first() +struct state { - static registered_test_base* ptr = 0; - return ptr; -} + bool is_quiet; + registered_test_base* first_test; + registered_test_base* last_test; -static inline registered_test_base*& last() -{ - static registered_test_base* ptr = 0; - return ptr; -} + state() : is_quiet(false), first_test(0), last_test(0) {} -static inline void add_test(registered_test_base* test) -{ - if (last()) { - last()->next = test; - } else { - first() = test; + void add_test(registered_test_base* test) + { + if (last_test) { + last_test->next = test; + } else { + first_test = test; + } + last_test = test; } - last() = test; -} + void run_tests(bool quiet = false) + { + is_quiet = quiet; -static inline void run_tests() -{ - for (registered_test_base* i = first(); i; i = i->next) { - std::cout << "Running " << i->name << "\n" << std::flush; - i->run(); - std::cerr << std::flush; - std::cout << std::flush; + for (registered_test_base* i = first_test; i; i = i->next) { + int error_count = boost::detail::test_errors(); + if (!quiet) { + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Running " << i->name << "\n" + << std::flush; + } + i->run(); + BOOST_LIGHTWEIGHT_TEST_OSTREAM << std::flush; + if (quiet && error_count != boost::detail::test_errors()) { + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Error in: " << i->name + << "\n" + << std::flush; + } + } } + + int start_sub_test(char const* name) + { + if (!is_quiet) { + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Sub-test: " << name << "\n" + << std::flush; + } + // Add one because it's used as a loop condition. + return boost::detail::test_errors() + 1; + } + + int end_sub_test(char const* name, int value) + { + if (is_quiet && value != boost::detail::test_errors() + 1) { + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Error in sub-test: " << name + << "\n" + << std::flush; + } + return 0; + } +}; + +// Get the currnet translation unit's test state. +static inline state& get_state() +{ + static state instance; + return instance; } } -inline void write_compiler_info() -{ -#if defined(BOOST_GCC_CXX11) - char const* cpp11 = "true"; +#if defined(__cplusplus) +#define BOOST_UNORDERED_CPLUSPLUS __cplusplus #else - char const* cpp11 = "false"; +#define BOOST_UNORDERED_CPLUSPLUS "(not defined)" #endif - std::cout << "Compiler: " << BOOST_COMPILER << "\n" - << "Library: " << BOOST_STDLIB << "\n" - << "C++11: " << cpp11 << "\n" - << "\n" - << std::flush; -} -} +#define BOOST_UNORDERED_TEST_COMPILER_INFO() \ + { \ + BOOST_LIGHTWEIGHT_TEST_OSTREAM \ + << "Compiler: " << BOOST_COMPILER << "\n" \ + << "Library: " << BOOST_STDLIB << "\n" \ + << "__cplusplus: " << BOOST_UNORDERED_CPLUSPLUS << "\n\n" \ + << "BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT: " \ + << BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT << "\n" \ + << "BOOST_UNORDERED_EMPLACE_LIMIT: " \ + << BOOST_UNORDERED_EMPLACE_LIMIT << "\n" \ + << "BOOST_UNORDERED_USE_ALLOCATOR_TRAITS: " \ + << BOOST_UNORDERED_USE_ALLOCATOR_TRAITS << "\n" \ + << "BOOST_UNORDERED_CXX11_CONSTRUCTION: " \ + << BOOST_UNORDERED_CXX11_CONSTRUCTION << "\n\n" \ + << std::flush; \ + } #include #include @@ -122,4 +175,31 @@ inline void write_compiler_info() #define UNORDERED_TEST_OP_JOIN(s, state, elem) \ BOOST_PP_CAT(state, BOOST_PP_CAT(_, elem)) +#define UNORDERED_MULTI_TEST(name, impl, parameters) \ + UNORDERED_MULTI_TEST_REPEAT(name, impl, 1, parameters) + +#define UNORDERED_MULTI_TEST_REPEAT(name, impl, n, parameters) \ + UNORDERED_AUTO_TEST(name) \ + { \ + BOOST_PP_SEQ_FOR_EACH_PRODUCT( \ + UNORDERED_MULTI_TEST_OP, ((impl))((n))parameters) \ + } + +#define UNORDERED_MULTI_TEST_OP(r, product) \ + UNORDERED_MULTI_TEST_OP2(BOOST_PP_SEQ_ELEM(0, product), \ + BOOST_PP_SEQ_ELEM(1, product), \ + BOOST_PP_SEQ_TAIL(BOOST_PP_SEQ_TAIL(product))) + +// Need to wrap UNORDERED_SUB_TEST in a block to avoid an msvc bug. +// https://support.microsoft.com/en-gb/help/315481/bug-too-many-unnested-loops-incorrectly-causes-a-c1061-compiler-error-in-visual-c +#define UNORDERED_MULTI_TEST_OP2(name, n, params) \ + { \ + UNORDERED_SUB_TEST(BOOST_PP_STRINGIZE( \ + BOOST_PP_SEQ_FOLD_LEFT(UNORDERED_TEST_OP_JOIN, name, params))) \ + { \ + for (int i = 0; i < n; ++i) \ + name BOOST_PP_SEQ_TO_TUPLE(params); \ + } \ + } + #endif diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp index d5f0f5db..d0286797 100644 --- a/test/objects/exception.hpp +++ b/test/objects/exception.hpp @@ -10,10 +10,11 @@ #include "../helpers/count.hpp" #include "../helpers/fwd.hpp" +#include "../helpers/generators.hpp" #include "../helpers/memory.hpp" +#include "./fwd.hpp" #include #include -#include #include namespace test { @@ -225,6 +226,55 @@ class hash } }; +class less +{ + int tag_; + + public: + less(int t = 0) : tag_(t) {} + + less(less const& x) : tag_(x.tag_) {} + + bool operator()(object const& x1, object const& x2) const + { + return less_impl(x1, x2); + } + + bool operator()(std::pair const& x1, + std::pair const& x2) const + { + if (less_impl(x1.first, x2.first)) { + return true; + } + if (!less_impl(x1.first, x2.first)) { + return false; + } + return less_impl(x1.second, x2.second); + } + + bool less_impl(object const& x1, object const& x2) const + { + switch (tag_) { + case 1: + return x1.tag1_ < x2.tag1_; + case 2: + return x1.tag2_ < x2.tag2_; + default: + return x1 < x2; + } + } + + friend bool operator==(less const& x1, less const& x2) + { + return x1.tag_ == x2.tag_; + } + + friend bool operator!=(less const& x1, less const& x2) + { + return x1.tag_ != x2.tag_; + } +}; + class equal_to { int tag_; @@ -309,6 +359,8 @@ class equal_to } return x1.tag_ != x2.tag_; } + + friend less create_compare(equal_to x) { return less(x.tag_); } }; template class allocator @@ -339,19 +391,11 @@ template class allocator template allocator(allocator const& x) : tag_(x.tag_) { - UNORDERED_SCOPE(allocator::allocator()) - { - UNORDERED_EPOINT("Mock allocator template copy constructor."); - } test::detail::tracker.allocator_ref(); } allocator(allocator const& x) : tag_(x.tag_) { - UNORDERED_SCOPE(allocator::allocator()) - { - UNORDERED_EPOINT("Mock allocator copy constructor."); - } test::detail::tracker.allocator_ref(); } @@ -359,11 +403,7 @@ template class allocator allocator& operator=(allocator const& x) { - UNORDERED_SCOPE(allocator::allocator()) - { - UNORDERED_EPOINT("Mock allocator assignment operator."); - tag_ = x.tag_; - } + tag_ = x.tag_; return *this; } @@ -530,42 +570,22 @@ template class allocator2 allocator2(allocator const& x) : tag_(x.tag_) { - UNORDERED_SCOPE(allocator2::allocator2()) - { - UNORDERED_EPOINT("Mock allocator2 constructor from allocator."); - } test::detail::tracker.allocator_ref(); } template allocator2(allocator2 const& x) : tag_(x.tag_) { - UNORDERED_SCOPE(allocator2::allocator2()) - { - UNORDERED_EPOINT("Mock allocator2 template copy constructor."); - } test::detail::tracker.allocator_ref(); } allocator2(allocator2 const& x) : tag_(x.tag_) { - UNORDERED_SCOPE(allocator2::allocator2()) - { - UNORDERED_EPOINT("Mock allocator2 copy constructor."); - } test::detail::tracker.allocator_ref(); } ~allocator2() { test::detail::tracker.allocator_unref(); } - allocator2& operator=(allocator2 const& x) - { - UNORDERED_SCOPE(allocator2::allocator2()) - { - UNORDERED_EPOINT("Mock allocator2 assignment operator."); - tag_ = x.tag_; - } - return *this; - } + allocator2& operator=(allocator2 const&) { return *this; } // If address throws, then it can't be used in erase or the // destructor, which is very limiting. I need to check up on @@ -704,6 +724,14 @@ inline bool operator!=(allocator2 const& x, allocator2 const& y) } } +namespace test { +template struct equals_to_compare; +template <> struct equals_to_compare +{ + typedef test::exception::less type; +}; +} + // Workaround for ADL deficient compilers #if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) namespace test { 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/assign_tests.cpp b/test/unordered/assign_tests.cpp index f0e3aaae..3aee2198 100644 --- a/test/unordered/assign_tests.cpp +++ b/test/unordered/assign_tests.cpp @@ -17,8 +17,6 @@ #include "../helpers/tracker.hpp" #include "../helpers/equivalent.hpp" -#include - #if defined(BOOST_MSVC) #pragma warning(disable : 4127) // conditional expression is constant #endif @@ -32,7 +30,7 @@ template void assign_tests1(T*, test::random_generator generator) BOOST_DEDUCED_TYPENAME T::hasher hf; BOOST_DEDUCED_TYPENAME T::key_equal eq; - std::cerr << "assign_tests1.1\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "assign_tests1.1\n"; { test::check_instances check_; @@ -43,7 +41,7 @@ template void assign_tests1(T*, test::random_generator generator) BOOST_TEST(test::equivalent(x.key_eq(), eq)); } - std::cerr << "assign_tests1.2\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "assign_tests1.2\n"; { test::check_instances check_; @@ -79,7 +77,7 @@ template void assign_tests2(T*, test::random_generator generator) typedef BOOST_DEDUCED_TYPENAME T::allocator_type allocator_type; - std::cerr << "assign_tests2.0 - empty container\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "assign_tests2.0 - empty container\n"; { test::check_instances check_; @@ -93,7 +91,7 @@ template void assign_tests2(T*, test::random_generator generator) test::check_container(x1, x2); } - std::cerr << "assign_tests2.1\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "assign_tests2.1\n"; { test::check_instances check_; @@ -110,7 +108,7 @@ template void assign_tests2(T*, test::random_generator generator) BOOST_TEST(x2.load_factor() <= x2.max_load_factor()); } - std::cerr << "assign_tests2.1a\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "assign_tests2.1a\n"; { test::check_instances check_; @@ -128,7 +126,7 @@ template void assign_tests2(T*, test::random_generator generator) BOOST_TEST(x2.load_factor() <= x2.max_load_factor()); } - std::cerr << "assign_tests2.2\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "assign_tests2.2\n"; { test::check_instances check_; @@ -150,7 +148,7 @@ template void assign_tests2(T*, test::random_generator generator) BOOST_TEST(x2.load_factor() <= x2.max_load_factor()); } - std::cerr << "assign_tests2.3\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "assign_tests2.3\n"; { test::check_instances check_; @@ -172,7 +170,7 @@ template void assign_tests2(T*, test::random_generator generator) BOOST_TEST(x2.load_factor() <= x2.max_load_factor()); } - std::cerr << "assign_tests2.4\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "assign_tests2.4\n"; { test::check_instances check_; @@ -271,7 +269,7 @@ UNORDERED_TEST( UNORDERED_AUTO_TEST(assign_default_initializer_list) { - std::cerr << "Initializer List Tests\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Initializer List Tests\n"; std::initializer_list > init; boost::unordered_map x1; x1[25] = 3; @@ -286,7 +284,7 @@ UNORDERED_AUTO_TEST(assign_default_initializer_list) #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) UNORDERED_AUTO_TEST(assign_initializer_list) { - std::cerr << "Initializer List Tests\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Initializer List Tests\n"; boost::unordered_set x; x.insert(10); diff --git a/test/unordered/at_tests.cpp b/test/unordered/at_tests.cpp index 11c0aa75..a948aeff 100644 --- a/test/unordered/at_tests.cpp +++ b/test/unordered/at_tests.cpp @@ -19,6 +19,21 @@ UNORDERED_AUTO_TEST(at_tests) BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Create Map" << std::endl; boost::unordered_map x; + boost::unordered_map const& x_const(x); + + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Check empty container" << std::endl; + + try { + x.at("one"); + BOOST_ERROR("Should have thrown."); + } catch (std::out_of_range) { + } + + try { + x_const.at("one"); + BOOST_ERROR("Should have thrown."); + } catch (std::out_of_range) { + } BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Add elements" << std::endl; @@ -29,6 +44,8 @@ UNORDERED_AUTO_TEST(at_tests) BOOST_TEST(x.at("one") == 1); BOOST_TEST(x.at("two") == 2); + BOOST_TEST(x_const.at("one") == 1); + BOOST_TEST(x_const.at("two") == 2); BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Check missing element" << std::endl; @@ -38,6 +55,12 @@ UNORDERED_AUTO_TEST(at_tests) } catch (std::out_of_range) { } + try { + x_const.at("three"); + BOOST_ERROR("Should have thrown."); + } catch (std::out_of_range) { + } + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Finished" << std::endl; } } diff --git a/test/unordered/bucket_tests.cpp b/test/unordered/bucket_tests.cpp index 280c013e..9cb5bdd4 100644 --- a/test/unordered/bucket_tests.cpp +++ b/test/unordered/bucket_tests.cpp @@ -37,7 +37,8 @@ template void tests(X*, test::random_generator generator) BOOST_TEST(x.bucket_count() <= x.max_bucket_count()); if (!(x.bucket_count() <= x.max_bucket_count())) { - std::cerr << x.bucket_count() << "<=" << x.max_bucket_count() << "\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << x.bucket_count() + << "<=" << x.max_bucket_count() << "\n"; } for (BOOST_DEDUCED_TYPENAME test::random_values::const_iterator diff --git a/test/unordered/compile_map.cpp b/test/unordered/compile_map.cpp index 1b099c70..863b56c1 100644 --- a/test/unordered/compile_map.cpp +++ b/test/unordered/compile_map.cpp @@ -12,7 +12,6 @@ #include "../helpers/postfix.hpp" // clang-format on -#include #include "../helpers/test.hpp" #include "../objects/minimal.hpp" #include "./compile_tests.hpp" @@ -46,7 +45,7 @@ UNORDERED_AUTO_TEST(test0) value_type; value_type value(x, x); - std::cout << "Test unordered_map.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n"; boost::unordered_map int_map; @@ -64,7 +63,7 @@ UNORDERED_AUTO_TEST(test0) container_test(int_map2, std::pair(0, 0)); container_test(map, value); - std::cout << "Test unordered_multimap.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n"; boost::unordered_multimap int_multimap; @@ -139,7 +138,7 @@ UNORDERED_AUTO_TEST(test1) int value = 0; std::pair map_value(0, 0); - std::cout << "Test unordered_map.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n"; boost::unordered_map map; @@ -157,7 +156,7 @@ UNORDERED_AUTO_TEST(test1) unordered_copyable_test(map2, value, map_value, hash, equal_to); unordered_map_functions(map2, value, value); - std::cout << "Test unordered_multimap.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n"; boost::unordered_multimap multimap; @@ -188,7 +187,7 @@ UNORDERED_AUTO_TEST(test2) map_value_type; map_value_type map_value(assignable, assignable); - std::cout << "Test unordered_map.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n"; boost::unordered_map, @@ -212,7 +211,7 @@ UNORDERED_AUTO_TEST(test2) unordered_map_functions(map2, assignable, default_assignable); - std::cout << "Test unordered_multimap.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n"; boost::unordered_multimap lwg2059_key(T v) : value(v) {} +}; + +std::size_t hash_value(lwg2059_key x) +{ + return static_cast(x.value); +} + +bool operator==(lwg2059_key x, lwg2059_key y) { return x.value == y.value; } + +UNORDERED_AUTO_TEST(lwg2059) +{ + { + boost::unordered_map x; + x.emplace(lwg2059_key(10), 5); + x.erase(x.begin()); + } + + { + boost::unordered_multimap x; + x.emplace(lwg2059_key(10), 5); + x.erase(x.begin()); + } +} + RUN_TESTS() diff --git a/test/unordered/compile_set.cpp b/test/unordered/compile_set.cpp index 3ea98703..9ea71f73 100644 --- a/test/unordered/compile_set.cpp +++ b/test/unordered/compile_set.cpp @@ -12,7 +12,6 @@ #include "../helpers/postfix.hpp" // clang-format on -#include #include "../helpers/test.hpp" #include "../objects/minimal.hpp" #include "./compile_tests.hpp" @@ -42,7 +41,7 @@ UNORDERED_AUTO_TEST(test0) test::minimal::assignable assignable(x); - std::cout << "Test unordered_set.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n"; boost::unordered_set int_set; @@ -60,7 +59,7 @@ UNORDERED_AUTO_TEST(test0) container_test(int_set2, 0); container_test(set, assignable); - std::cout << "Test unordered_multiset.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n"; boost::unordered_multiset int_multiset; @@ -128,7 +127,7 @@ UNORDERED_AUTO_TEST(test1) std::equal_to equal_to; int value = 0; - std::cout << "Test unordered_set." << std::endl; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set." << std::endl; boost::unordered_set set; @@ -144,7 +143,7 @@ UNORDERED_AUTO_TEST(test1) unordered_set_test(set2, value); unordered_copyable_test(set2, value, value, hash, equal_to); - std::cout << "Test unordered_multiset." << std::endl; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset." << std::endl; boost::unordered_multiset multiset; @@ -170,7 +169,7 @@ UNORDERED_AUTO_TEST(test2) test::minimal::hash hash(x); test::minimal::equal_to equal_to(x); - std::cout << "Test unordered_set.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n"; boost::unordered_set, @@ -183,7 +182,7 @@ UNORDERED_AUTO_TEST(test2) unordered_copyable_test(set, assignable, assignable, hash, equal_to); unordered_set_member_test(set, assignable); - std::cout << "Test unordered_multiset.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n"; boost::unordered_multiset, @@ -205,7 +204,7 @@ UNORDERED_AUTO_TEST(movable1_tests) test::minimal::hash hash(x); test::minimal::equal_to equal_to(x); - std::cout << "Test unordered_set.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n"; boost::unordered_set, @@ -217,7 +216,7 @@ UNORDERED_AUTO_TEST(movable1_tests) unordered_set_test(set, movable1); unordered_movable_test(set, movable1, movable1, hash, equal_to); - std::cout << "Test unordered_multiset.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n"; boost::unordered_multiset, @@ -238,7 +237,7 @@ UNORDERED_AUTO_TEST(movable2_tests) test::minimal::hash hash(x); test::minimal::equal_to equal_to(x); - std::cout << "Test unordered_set.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n"; boost::unordered_set, @@ -250,7 +249,7 @@ UNORDERED_AUTO_TEST(movable2_tests) unordered_set_test(set, movable2); unordered_movable_test(set, movable2, movable2, hash, equal_to); - std::cout << "Test unordered_multiset.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n"; boost::unordered_multiset, @@ -271,7 +270,7 @@ UNORDERED_AUTO_TEST(destructible_tests) test::minimal::hash hash(x); test::minimal::equal_to equal_to(x); - std::cout << "Test unordered_set.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n"; boost::unordered_set, @@ -280,7 +279,7 @@ UNORDERED_AUTO_TEST(destructible_tests) unordered_destructible_test(set); - std::cout << "Test unordered_multiset.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n"; boost::unordered_multiset, @@ -290,4 +289,36 @@ UNORDERED_AUTO_TEST(destructible_tests) unordered_destructible_test(multiset); } +// Test for ambiguity when using key convertible from iterator +// See LWG2059 + +struct lwg2059_key +{ + int value; + + template lwg2059_key(T v) : value(v) {} +}; + +std::size_t hash_value(lwg2059_key x) +{ + return static_cast(x.value); +} + +bool operator==(lwg2059_key x, lwg2059_key y) { return x.value == y.value; } + +UNORDERED_AUTO_TEST(lwg2059) +{ + { + boost::unordered_set x; + x.emplace(lwg2059_key(10)); + x.erase(x.begin()); + } + + { + boost::unordered_multiset x; + x.emplace(lwg2059_key(10)); + x.erase(x.begin()); + } +} + RUN_TESTS() diff --git a/test/unordered/compile_tests.hpp b/test/unordered/compile_tests.hpp index fb908ffd..af913614 100644 --- a/test/unordered/compile_tests.hpp +++ b/test/unordered/compile_tests.hpp @@ -19,7 +19,9 @@ #include "../helpers/check_return_type.hpp" #include #include +#include #include +#include #include #include #include @@ -30,6 +32,23 @@ template void sink(T const&) {} template T rvalue(T const& v) { return v; } template T rvalue_default() { return T(); } +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) +template T implicit_construct() { return {}; } +#else +template int implicit_construct() +{ + T x; + sink(x); + return 0; +} +#endif + +#if !defined(BOOST_NO_CXX11_NOEXCEPT) +#define TEST_NOEXCEPT_EXPR(x) BOOST_STATIC_ASSERT((BOOST_NOEXCEPT_EXPR(x))); +#else +#define TEST_NOEXCEPT_EXPR(x) +#endif + template void container_test(X& r, T const&) { typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; @@ -52,6 +71,8 @@ template void container_test(X& r, T const&) typedef BOOST_DEDUCED_TYPENAME X::node_type node_type; + typedef BOOST_DEDUCED_TYPENAME X::allocator_type allocator_type; + // value_type BOOST_STATIC_ASSERT((boost::is_same::value)); @@ -75,7 +96,9 @@ template void container_test(X& r, T const&) BOOST_STATIC_ASSERT((boost::is_same::value)); // node_type - // TODO? + + BOOST_STATIC_ASSERT((boost::is_same::value)); // difference_type @@ -134,7 +157,6 @@ template void container_test(X& r, T const&) // Allocator - typedef BOOST_DEDUCED_TYPENAME X::allocator_type allocator_type; test::check_return_type::equals(a_const.get_allocator()); allocator_type m = a.get_allocator(); @@ -147,21 +169,30 @@ template void container_test(X& r, T const&) // node_type - typedef BOOST_DEDUCED_TYPENAME X::node_type node_type; - BOOST_STATIC_ASSERT((boost::is_same::value)); + implicit_construct(); +#if !BOOST_COMP_GNUC || BOOST_COMP_GNUC >= BOOST_VERSION_NUMBER(4, 8, 0) + TEST_NOEXCEPT_EXPR(node_type()); +#endif node_type n1; node_type n2(rvalue_default()); +#if !BOOST_COMP_GNUC || BOOST_COMP_GNUC >= BOOST_VERSION_NUMBER(4, 8, 0) + TEST_NOEXCEPT_EXPR(node_type(boost::move(n1))); +#endif node_type n3; n3 = boost::move(n2); n1.swap(n3); swap(n1, n3); + // TODO: noexcept for swap? + // value, key, mapped tests in map and set specific testing. node_type const n_const; BOOST_TEST(n_const ? 0 : 1); + TEST_NOEXCEPT_EXPR(n_const ? 0 : 1); test::check_return_type::equals(!n_const); test::check_return_type::equals(n_const.empty()); + TEST_NOEXCEPT_EXPR(!n_const); + TEST_NOEXCEPT_EXPR(n_const.empty()); // Avoid unused variable warnings: @@ -302,14 +333,22 @@ void unordered_map_test(X& r, Key const& k, T const& v) BOOST_STATIC_ASSERT((boost::is_same::value)); BOOST_STATIC_ASSERT((boost::is_same::value)); + // Superfluous,but just to make sure. + BOOST_STATIC_ASSERT((!boost::is_const::value)); // Calling functions r.insert(std::pair(k, v)); + r.insert(r.begin(), std::pair(k, v)); + std::pair const value(k, v); + r.insert(value); + r.insert(r.end(), value); Key k_lvalue(k); T v_lvalue(v); + // Emplace + r.emplace(k, v); r.emplace(k_lvalue, v_lvalue); r.emplace(rvalue(k), rvalue(v)); @@ -317,6 +356,17 @@ void unordered_map_test(X& r, Key const& k, T const& v) r.emplace(boost::unordered::piecewise_construct, boost::make_tuple(k), boost::make_tuple(v)); + // Emplace with hint + + r.emplace_hint(r.begin(), k, v); + r.emplace_hint(r.begin(), k_lvalue, v_lvalue); + r.emplace_hint(r.begin(), rvalue(k), rvalue(v)); + + r.emplace_hint(r.begin(), boost::unordered::piecewise_construct, + boost::make_tuple(k), boost::make_tuple(v)); + + // Extract + test::check_return_type::equals(r.extract(r.begin())); r.emplace(k, v); @@ -327,8 +377,16 @@ void unordered_map_test(X& r, Key const& k, T const& v) test::check_return_type::equals_ref(n1.key()); test::check_return_type::equals_ref(n1.mapped()); - r.insert(boost::move(n1)); + node_type n2 = boost::move(n1); + r.insert(boost::move(n2)); + r.insert(r.extract(r.begin())); + n2 = r.extract(r.begin()); + r.insert(r.begin(), boost::move(n2)); r.insert(r.end(), r.extract(r.begin())); + + node_type n = r.extract(r.begin()); + test::check_return_type::equals_ref(n.key()); + test::check_return_type::equals_ref(n.mapped()); } template void equality_test(X& r) @@ -385,6 +443,7 @@ void unordered_map_functions(X&, Key const& k, T const& v) X a; test::check_return_type::equals_ref(a[k]); + test::check_return_type::equals_ref(a[rvalue(k)]); test::check_return_type::equals_ref(a.at(k)); test::check_return_type >::equals( a.try_emplace(k, v)); @@ -517,6 +576,7 @@ void unordered_test(X& x, Key& k, Hash& hf, Pred& eq) const_iterator q1 = a.cbegin(), q2 = a.cend(); test::check_return_type::equals(a.erase(q1, q2)); + TEST_NOEXCEPT_EXPR(a.clear()); a.clear(); X const b; @@ -627,6 +687,13 @@ void unordered_copyable_test(X& x, Key& k, T& t, Hash& hf, Pred& eq) sink(X(b, m)); X a9a(b, m); + X b1; + b1.insert(t); + X a9b(b1); + sink(a9b); + X a9c(b1, m); + sink(a9c); + const_iterator q = a.cbegin(); test::check_return_type::equals(a.insert(q, t)); @@ -638,7 +705,9 @@ void unordered_copyable_test(X& x, Key& k, T& t, Hash& hf, Pred& eq) a.insert(list); a.insert({t, t, t}); -#if !BOOST_WORKAROUND(BOOST_MSVC, < 1900) +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1900) && \ + (!defined(__clang__) || __clang_major__ >= 4 || \ + (__clang_major__ == 3 && __clang_minor__ >= 4)) a.insert({}); a.insert({t}); a.insert({t, t}); diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index 25901353..4c967ded 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -28,7 +28,7 @@ template void constructor_tests1(T*, test::random_generator generator) BOOST_DEDUCED_TYPENAME T::key_equal eq; BOOST_DEDUCED_TYPENAME T::allocator_type al; - std::cerr << "Construct 1\n"; + UNORDERED_SUB_TEST("Construct 1") { test::check_instances check_; @@ -40,7 +40,7 @@ template void constructor_tests1(T*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 2\n"; + UNORDERED_SUB_TEST("Construct 2") { test::check_instances check_; @@ -53,7 +53,7 @@ template void constructor_tests1(T*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 3\n"; + UNORDERED_SUB_TEST("Construct 3") { test::check_instances check_; @@ -66,7 +66,7 @@ template void constructor_tests1(T*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 4\n"; + UNORDERED_SUB_TEST("Construct 4") { test::check_instances check_; @@ -78,7 +78,7 @@ template void constructor_tests1(T*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 5\n"; + UNORDERED_SUB_TEST("Construct 5") { test::check_instances check_; @@ -92,7 +92,7 @@ template void constructor_tests1(T*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 6\n"; + UNORDERED_SUB_TEST("Construct 6") { test::check_instances check_; @@ -106,7 +106,7 @@ template void constructor_tests1(T*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 7\n"; + UNORDERED_SUB_TEST("Construct 7") { test::check_instances check_; @@ -120,7 +120,7 @@ template void constructor_tests1(T*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 8\n"; + UNORDERED_SUB_TEST("Construct 8") { test::check_instances check_; @@ -133,7 +133,7 @@ template void constructor_tests1(T*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 9\n"; + UNORDERED_SUB_TEST("Construct 9") { test::check_instances check_; @@ -145,7 +145,7 @@ template void constructor_tests1(T*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 10\n"; + UNORDERED_SUB_TEST("Construct 10") { test::check_instances check_; @@ -159,7 +159,7 @@ template void constructor_tests1(T*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 11\n"; + UNORDERED_SUB_TEST("Construct 11") { test::check_instances check_; @@ -185,7 +185,7 @@ void constructor_tests2(T*, test::random_generator const& generator) BOOST_DEDUCED_TYPENAME T::allocator_type al1(1); BOOST_DEDUCED_TYPENAME T::allocator_type al2(2); - std::cerr << "Construct 1\n"; + UNORDERED_SUB_TEST("Construct 1") { test::check_instances check_; T x(10000, hf1, eq1); @@ -196,7 +196,7 @@ void constructor_tests2(T*, test::random_generator const& generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 2\n"; + UNORDERED_SUB_TEST("Construct 2") { test::check_instances check_; T x(100, hf1); @@ -208,7 +208,7 @@ void constructor_tests2(T*, test::random_generator const& generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 3\n"; + UNORDERED_SUB_TEST("Construct 3") { test::check_instances check_; test::random_values v(100, generator); @@ -220,7 +220,7 @@ void constructor_tests2(T*, test::random_generator const& generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 4\n"; + UNORDERED_SUB_TEST("Construct 4") { test::check_instances check_; test::random_values v(5, generator); @@ -233,7 +233,7 @@ void constructor_tests2(T*, test::random_generator const& generator) test::check_equivalent_keys(x); } - std::cerr << "Construct 5\n"; + UNORDERED_SUB_TEST("Construct 5") { test::check_instances check_; test::random_values v(100, generator); @@ -245,7 +245,7 @@ void constructor_tests2(T*, test::random_generator const& generator) test::check_equivalent_keys(y); } - std::cerr << "Construct 6\n"; + UNORDERED_SUB_TEST("Construct 6") { test::check_instances check_; test::random_values v(100, generator); @@ -257,7 +257,7 @@ void constructor_tests2(T*, test::random_generator const& generator) test::check_equivalent_keys(y); } - std::cerr << "Construct 7\n"; + UNORDERED_SUB_TEST("Construct 7") { test::check_instances check_; test::random_values v(100, generator); @@ -269,7 +269,7 @@ void constructor_tests2(T*, test::random_generator const& generator) test::check_equivalent_keys(y); } - std::cerr << "Construct 8 - from input iterator\n"; + UNORDERED_SUB_TEST("Construct 8 - from input iterator") { test::check_instances check_; test::random_values v(100, generator); @@ -288,7 +288,7 @@ void constructor_tests2(T*, test::random_generator const& generator) test::check_equivalent_keys(y); } - std::cerr << "Construct 8.5 - from copy iterator\n"; + UNORDERED_SUB_TEST("Construct 8.5 - from copy iterator") { test::check_instances check_; test::random_values v(100, generator); @@ -302,7 +302,7 @@ void constructor_tests2(T*, test::random_generator const& generator) test::check_equivalent_keys(y); } - std::cerr << "Construct 9\n"; + UNORDERED_SUB_TEST("Construct 9") { test::check_instances check_; @@ -320,7 +320,7 @@ void constructor_tests2(T*, test::random_generator const& generator) #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) std::initializer_list list; - std::cerr << "Initializer list construct 1\n"; + UNORDERED_SUB_TEST("Initializer list construct 1") { test::check_instances check_; @@ -331,7 +331,7 @@ void constructor_tests2(T*, test::random_generator const& generator) BOOST_TEST(test::equivalent(x.get_allocator(), al)); } - std::cerr << "Initializer list construct 2\n"; + UNORDERED_SUB_TEST("Initializer list construct 2") { test::check_instances check_; @@ -343,7 +343,7 @@ void constructor_tests2(T*, test::random_generator const& generator) BOOST_TEST(test::equivalent(x.get_allocator(), al)); } - std::cerr << "Initializer list construct 3\n"; + UNORDERED_SUB_TEST("Initializer list construct 3") { test::check_instances check_; @@ -355,7 +355,7 @@ void constructor_tests2(T*, test::random_generator const& generator) BOOST_TEST(test::equivalent(x.get_allocator(), al)); } - std::cerr << "Initializer list construct 4\n"; + UNORDERED_SUB_TEST("Initializer list construct 4") { test::check_instances check_; @@ -367,7 +367,7 @@ void constructor_tests2(T*, test::random_generator const& generator) BOOST_TEST(test::equivalent(x.get_allocator(), al)); } - std::cerr << "Initializer list construct 5\n"; + UNORDERED_SUB_TEST("Initializer list construct 5") { test::check_instances check_; @@ -384,8 +384,6 @@ void constructor_tests2(T*, test::random_generator const& generator) template void map_constructor_test(T*, test::random_generator const& generator) { - std::cerr << "map_constructor_test\n"; - typedef test::list > list; @@ -429,7 +427,6 @@ UNORDERED_TEST(map_constructor_test, UNORDERED_AUTO_TEST(test_default_initializer_list) { - std::cerr << "Initializer List Tests\n"; std::initializer_list init; boost::unordered_set x1 = init; BOOST_TEST(x1.empty()); @@ -441,7 +438,6 @@ UNORDERED_AUTO_TEST(test_default_initializer_list) UNORDERED_AUTO_TEST(test_initializer_list) { - std::cerr << "Initializer List Tests\n"; boost::unordered_set x1 = {2, 10, 45, -5}; BOOST_TEST(x1.find(10) != x1.end()); BOOST_TEST(x1.find(46) == x1.end()); @@ -450,4 +446,4 @@ UNORDERED_AUTO_TEST(test_initializer_list) #endif } -RUN_TESTS() +RUN_TESTS_QUIET() diff --git a/test/unordered/detail_tests.cpp b/test/unordered/detail_tests.cpp new file mode 100644 index 00000000..2af4c4fb --- /dev/null +++ b/test/unordered/detail_tests.cpp @@ -0,0 +1,102 @@ + +// Copyright 2017 Daniel James. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// clang-format off +#include "../helpers/prefix.hpp" +#include +#include +#include "../helpers/postfix.hpp" +// clang-format on + +#include "../helpers/test.hpp" +#include + +// Pretty inefficient, but the test is fast enough. +// Might be too slow if we had larger primes? +bool is_prime(std::size_t x) +{ + if (x == 2) { + return true; + } else if (x == 1 || x % 2 == 0) { + return false; + } else { + // y*y <= x had rounding errors, so instead use y <= (x/y). + for (std::size_t y = 3; y <= (x / y); y += 2) { + if (x % y == 0) { + return false; + break; + } + } + + return true; + } +} + +void test_next_prime(std::size_t value) +{ + std::size_t x = boost::unordered::detail::next_prime(value); + BOOST_TEST(is_prime(x)); + BOOST_TEST(x >= value); +} + +void test_prev_prime(std::size_t value) +{ + std::size_t x = boost::unordered::detail::prev_prime(value); + BOOST_TEST(is_prime(x)); + BOOST_TEST(x <= value); + if (x > value) { + BOOST_LIGHTWEIGHT_TEST_OSTREAM << x << "," << value << std::endl; + } +} + +UNORDERED_AUTO_TEST(next_prime_test) +{ + BOOST_TEST(!is_prime(0)); + BOOST_TEST(!is_prime(1)); + BOOST_TEST(is_prime(2)); + BOOST_TEST(is_prime(3)); + BOOST_TEST(is_prime(13)); + BOOST_TEST(!is_prime(4)); + BOOST_TEST(!is_prime(100)); + + BOOST_TEST(boost::unordered::detail::next_prime(0) > 0); + + // test_prev_prime doesn't work for values less than 17. + // Which should be okay, unless an allocator has a really tiny + // max_size? + const std::size_t min_prime = 17; + + // test_next_prime doesn't work for values greater than this, + // which might be a problem if you've got terrabytes of memory? + // I seriously doubt the container would work well at such sizes + // regardless. + const std::size_t max_prime = 4294967291ul; + + std::size_t i; + + BOOST_TEST(is_prime(min_prime)); + BOOST_TEST(is_prime(max_prime)); + + for (i = 0; i < 10000; ++i) { + if (i < min_prime) { + BOOST_TEST(boost::unordered::detail::prev_prime(i) == min_prime); + } else { + test_prev_prime(i); + } + test_next_prime(i); + } + + std::size_t last = i - 1; + for (; i > last; last = i, i += i / 5) { + if (i > max_prime) { + BOOST_TEST(boost::unordered::detail::next_prime(i) == max_prime); + } else { + test_next_prime(i); + } + test_prev_prime(i); + } +} + +RUN_TESTS() diff --git a/test/unordered/equivalent_keys_tests.cpp b/test/unordered/equivalent_keys_tests.cpp index 72ab057e..8e48d68c 100644 --- a/test/unordered/equivalent_keys_tests.cpp +++ b/test/unordered/equivalent_keys_tests.cpp @@ -17,8 +17,6 @@ #include "../helpers/tracker.hpp" #include "../helpers/invariants.hpp" -#include - template void test_equal_insertion(Iterator begin, Iterator end) { diff --git a/test/unordered/erase_equiv_tests.cpp b/test/unordered/erase_equiv_tests.cpp index 1907ddd1..64510394 100644 --- a/test/unordered/erase_equiv_tests.cpp +++ b/test/unordered/erase_equiv_tests.cpp @@ -17,7 +17,6 @@ #include "../helpers/invariants.hpp" #include "../helpers/helpers.hpp" #include -#include #include #include "../objects/test.hpp" @@ -31,14 +30,15 @@ struct write_pair_type template void operator()(std::pair const& x) const { - std::cout << "(" << x.first << "," << x.second << ")"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "(" << x.first << "," << x.second + << ")"; } } write_pair; template void write_container(Container const& x) { std::for_each(x.begin(), x.end(), write_pair); - std::cout << "\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "\n"; } // Make everything collide - for testing erase in a single bucket. @@ -165,8 +165,8 @@ template void erase_subrange_tests(Container const& x) collide_list init(y.begin(), y.end()); if (!general_erase_range_test(y, position, position + length)) { BOOST_ERROR("general_erase_range_test failed."); - std::cout << "Erase: [" << position << "," << position + length - << ")\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Erase: [" << position << "," + << position + length << ")\n"; write_container(init); write_container(y); } @@ -185,7 +185,8 @@ void x_by_y_erase_range_tests(Container*, int values, int duplicates) } } - std::cout << "Values: " << values << ", Duplicates: " << duplicates << "\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Values: " << values + << ", Duplicates: " << duplicates << "\n"; erase_subrange_tests(y); } @@ -201,24 +202,24 @@ void exhaustive_erase_tests(Container* x, int num_values, int num_duplicated) UNORDERED_AUTO_TEST(exhaustive_collide_tests) { - std::cout << "exhaustive_collide_tests:\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "exhaustive_collide_tests:\n"; collide_map m; exhaustive_erase_tests((collide_map*)0, 4, 4); - std::cout << "\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "\n"; } UNORDERED_AUTO_TEST(exhaustive_collide2_tests) { - std::cout << "exhaustive_collide2_tests:\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "exhaustive_collide2_tests:\n"; exhaustive_erase_tests((collide_map2*)0, 8, 4); - std::cout << "\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "\n"; } UNORDERED_AUTO_TEST(exhaustive_collide3_tests) { - std::cout << "exhaustive_collide3_tests:\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "exhaustive_collide3_tests:\n"; exhaustive_erase_tests((collide_map3*)0, 8, 4); - std::cout << "\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "\n"; } RUN_TESTS() diff --git a/test/unordered/erase_tests.cpp b/test/unordered/erase_tests.cpp index c763c66f..843e64f2 100644 --- a/test/unordered/erase_tests.cpp +++ b/test/unordered/erase_tests.cpp @@ -18,7 +18,6 @@ #include "../helpers/helpers.hpp" #include "../helpers/invariants.hpp" #include -#include #include namespace erase_tests { @@ -31,7 +30,7 @@ void erase_tests1(Container*, test::random_generator generator) typedef BOOST_DEDUCED_TYPENAME Container::iterator iterator; typedef BOOST_DEDUCED_TYPENAME Container::const_iterator c_iterator; - std::cerr << "Erase by key.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Erase by key.\n"; { test::check_instances check_; @@ -52,7 +51,7 @@ void erase_tests1(Container*, test::random_generator generator) } } - std::cerr << "erase(begin()).\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "erase(begin()).\n"; { test::check_instances check_; @@ -75,7 +74,7 @@ void erase_tests1(Container*, test::random_generator generator) BOOST_TEST(x.empty()); } - std::cerr << "erase(random position).\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "erase(random position).\n"; { test::check_instances check_; @@ -104,7 +103,8 @@ void erase_tests1(Container*, test::random_generator generator) index == 0 ? next == x.begin() : next == test::next(prev)); BOOST_TEST(x.count(key) == count - 1); if (x.count(key) != count - 1) { - std::cerr << count << " => " << x.count(key) << std::endl; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << count << " => " + << x.count(key) << std::endl; } BOOST_TEST(x.size() == size); if (++iterations % 20 == 0) @@ -113,7 +113,7 @@ void erase_tests1(Container*, test::random_generator generator) BOOST_TEST(x.empty()); } - std::cerr << "erase(ranges).\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "erase(ranges).\n"; { test::check_instances check_; @@ -140,7 +140,7 @@ void erase_tests1(Container*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "erase(random ranges).\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "erase(random ranges).\n"; { test::check_instances check_; Container x; @@ -179,7 +179,7 @@ void erase_tests1(Container*, test::random_generator generator) } } - std::cerr << "quick_erase(begin()).\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "quick_erase(begin()).\n"; { test::check_instances check_; @@ -201,7 +201,7 @@ void erase_tests1(Container*, test::random_generator generator) BOOST_TEST(x.empty()); } - std::cerr << "quick_erase(random position).\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "quick_erase(random position).\n"; { test::check_instances check_; @@ -230,7 +230,8 @@ void erase_tests1(Container*, test::random_generator generator) index == 0 ? next == x.begin() : next == test::next(prev)); BOOST_TEST(x.count(key) == count - 1); if (x.count(key) != count - 1) { - std::cerr << count << " => " << x.count(key) << std::endl; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << count << " => " + << x.count(key) << std::endl; } BOOST_TEST(x.size() == size); if (++iterations % 20 == 0) @@ -239,7 +240,7 @@ void erase_tests1(Container*, test::random_generator generator) BOOST_TEST(x.empty()); } - std::cerr << "clear().\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "clear().\n"; { test::check_instances check_; @@ -250,7 +251,7 @@ void erase_tests1(Container*, test::random_generator generator) BOOST_TEST(x.begin() == x.end()); } - std::cerr << "\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "\n"; } boost::unordered_set -#include - namespace extract_tests { test::seed_t initialize_seed(85638); @@ -28,7 +26,7 @@ test::seed_t initialize_seed(85638); template void extract_tests1(Container*, test::random_generator generator) { - std::cerr << "Extract by key.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Extract by key.\n"; { test::check_instances check_; @@ -58,7 +56,7 @@ void extract_tests1(Container*, test::random_generator generator) BOOST_TEST(x.empty()); } - std::cerr << "extract(begin()).\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "extract(begin()).\n"; { test::check_instances check_; @@ -81,7 +79,7 @@ void extract_tests1(Container*, test::random_generator generator) BOOST_TEST(x.empty()); } - std::cerr << "extract(random position).\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "extract(random position).\n"; { test::check_instances check_; @@ -117,7 +115,7 @@ void extract_tests1(Container*, test::random_generator generator) BOOST_TEST(x.empty()); } - std::cerr << "\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "\n"; } boost::unordered_set struct allocator : std::allocator allocator(const allocator& other) : std::allocator(other) { } + + template + allocator(const std::allocator& other) : std::allocator(other) + { + } }; // Declare some members of a structs. diff --git a/test/unordered/insert_hint_tests.cpp b/test/unordered/insert_hint_tests.cpp index ea9c9b8e..5b4268bb 100644 --- a/test/unordered/insert_hint_tests.cpp +++ b/test/unordered/insert_hint_tests.cpp @@ -15,7 +15,6 @@ #include #include -#include namespace insert_hint { UNORDERED_AUTO_TEST(insert_hint_empty) diff --git a/test/unordered/insert_stable_tests.cpp b/test/unordered/insert_stable_tests.cpp index c85dd188..9915c10b 100644 --- a/test/unordered/insert_stable_tests.cpp +++ b/test/unordered/insert_stable_tests.cpp @@ -12,8 +12,6 @@ #include "../helpers/test.hpp" -#include - namespace insert_stable { struct member { @@ -47,9 +45,9 @@ std::size_t hash_value(insert_stable::member const& x) } } -// This is now only supported when using grouped nodes. I can't see any -// efficient way to do it otherwise. -#if !BOOST_UNORDERED_INTEROPERABLE_NODES +// This is no longer supported, as there's no longer an efficient way to get to +// the end of a group of equivalent nodes. +#if 0 UNORDERED_AUTO_TEST(stable_insert_test1) { diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index ae50fa18..3f68d316 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -3,6 +3,8 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#if !defined(PIECEWISE_TEST_NAME) + // clang-format off #include "../helpers/prefix.hpp" #include @@ -19,8 +21,6 @@ #include "../helpers/input_iterator.hpp" #include "../helpers/helpers.hpp" -#include - namespace insert_tests { test::seed_t initialize_seed(243432); @@ -33,66 +33,138 @@ void unique_insert_tests1(X*, test::random_generator generator) typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; typedef test::ordered ordered; - std::cerr << "insert(value) tests for containers with unique keys.\n"; + UNORDERED_SUB_TEST("insert(value) tests for containers with unique keys") + { + X x; + test::ordered tracker = test::create_ordered(x); - X x; - test::ordered tracker = test::create_ordered(x); + test::random_values v(1000, generator); - test::random_values v(1000, generator); + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { - for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = v.begin(); - it != v.end(); ++it) { + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); - BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = x.bucket_count(); - float b = x.max_load_factor(); + std::pair r1 = x.insert(*it); + std::pair r2 = + tracker.insert(*it); - std::pair r1 = x.insert(*it); - std::pair r2 = - tracker.insert(*it); + BOOST_TEST(r1.second == r2.second); + BOOST_TEST(*r1.first == *r2.first); - BOOST_TEST(r1.second == r2.second); - BOOST_TEST(*r1.first == *r2.first); + tracker.compare_key(x, *it); - tracker.compare_key(x, *it); + if (static_cast(x.size()) <= + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } - if (static_cast(x.size()) <= - b * static_cast(old_bucket_count)) - BOOST_TEST(x.bucket_count() == old_bucket_count); + test::check_equivalent_keys(x); } - test::check_equivalent_keys(x); + UNORDERED_SUB_TEST("insert(rvalue) tests for containers with unique keys") + { + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000, generator); + + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { + + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); + + typename X::value_type value = *it; + std::pair r1 = x.insert(boost::move(value)); + std::pair r2 = + tracker.insert(*it); + + BOOST_TEST(r1.second == r2.second); + BOOST_TEST(*r1.first == *r2.first); + + tracker.compare_key(x, *it); + + if (static_cast(x.size()) <= + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); + } } template void equivalent_insert_tests1(X*, test::random_generator generator) { - std::cerr << "insert(value) tests for containers with equivalent keys.\n"; - test::check_instances check_; - X x; - test::ordered tracker = test::create_ordered(x); + UNORDERED_SUB_TEST( + "insert(value) tests for containers with equivalent keys") + { + X x; + test::ordered tracker = test::create_ordered(x); - test::random_values v(1000, generator); - for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = v.begin(); - it != v.end(); ++it) { - BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = x.bucket_count(); - float b = x.max_load_factor(); + test::random_values v(1000, generator); + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); - BOOST_DEDUCED_TYPENAME X::iterator r1 = x.insert(*it); - BOOST_DEDUCED_TYPENAME test::ordered::iterator r2 = - tracker.insert(*it); + BOOST_DEDUCED_TYPENAME X::iterator r1 = x.insert(*it); + BOOST_DEDUCED_TYPENAME test::ordered::iterator r2 = + tracker.insert(*it); - BOOST_TEST(*r1 == *r2); + BOOST_TEST(*r1 == *r2); - tracker.compare_key(x, *it); + tracker.compare_key(x, *it); - if (static_cast(x.size()) <= - b * static_cast(old_bucket_count)) - BOOST_TEST(x.bucket_count() == old_bucket_count); + if (static_cast(x.size()) <= + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); } - test::check_equivalent_keys(x); + UNORDERED_SUB_TEST( + "insert(rvalue) tests for containers with equivalent keys") + { + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000, generator); + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); + + typename X::value_type value = *it; + BOOST_DEDUCED_TYPENAME X::iterator r1 = + x.insert(boost::move(value)); + BOOST_DEDUCED_TYPENAME test::ordered::iterator r2 = + tracker.insert(*it); + + BOOST_TEST(*r1 == *r2); + + tracker.compare_key(x, *it); + + if (static_cast(x.size()) <= + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); + } } template void insert_tests2(X*, test::random_generator generator) @@ -102,8 +174,7 @@ template void insert_tests2(X*, test::random_generator generator) typedef BOOST_DEDUCED_TYPENAME X::const_iterator const_iterator; typedef BOOST_DEDUCED_TYPENAME tracker_type::iterator tracker_iterator; - std::cerr << "insert(begin(), value) tests.\n"; - + UNORDERED_SUB_TEST("insert(begin(), value) tests") { test::check_instances check_; @@ -132,8 +203,7 @@ template void insert_tests2(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "insert(end(), value) tests.\n"; - + UNORDERED_SUB_TEST("insert(end(), value) tests") { test::check_instances check_; @@ -163,8 +233,7 @@ template void insert_tests2(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "insert(pos, value) tests.\n"; - + UNORDERED_SUB_TEST("insert(pos, value) tests") { test::check_instances check_; @@ -194,8 +263,38 @@ template void insert_tests2(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "insert single item range tests.\n"; + UNORDERED_SUB_TEST("insert(pos, rvalue) tests") + { + test::check_instances check_; + X x; + const_iterator pos = x.begin(); + tracker_type tracker = test::create_ordered(x); + + test::random_values v(1000, generator); + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); + + typename X::value_type value = *it; + pos = x.insert(pos, boost::move(value)); + tracker_iterator r2 = tracker.insert(tracker.begin(), *it); + BOOST_TEST(*pos == *r2); + tracker.compare_key(x, *it); + + if (static_cast(x.size()) <= + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + tracker.compare(x); + test::check_equivalent_keys(x); + } + + UNORDERED_SUB_TEST("insert single item range tests") { test::check_instances check_; @@ -223,8 +322,7 @@ template void insert_tests2(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "insert range tests.\n"; - + UNORDERED_SUB_TEST("insert range tests") { test::check_instances check_; @@ -237,8 +335,7 @@ template void insert_tests2(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "insert range with rehash tests.\n"; - + UNORDERED_SUB_TEST("insert range with rehash tests") { test::check_instances check_; @@ -255,8 +352,7 @@ template void insert_tests2(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "insert input iterator range tests.\n"; - + UNORDERED_SUB_TEST("insert input iterator range tests") { test::check_instances check_; @@ -272,8 +368,7 @@ template void insert_tests2(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "insert copy iterator range tests.\n"; - + UNORDERED_SUB_TEST("insert copy iterator range tests") { test::check_instances check_; @@ -286,8 +381,7 @@ template void insert_tests2(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "insert copy iterator range test 2.\n"; - + UNORDERED_SUB_TEST("insert copy iterator range test 2") { test::check_instances check_; @@ -303,8 +397,7 @@ template void insert_tests2(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "insert various ranges.\n"; - + UNORDERED_SUB_TEST("insert various ranges") { for (int i = 0; i < 100; ++i) { X x; @@ -350,8 +443,6 @@ void unique_emplace_tests1(X*, test::random_generator generator) typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; typedef test::ordered ordered; - std::cerr << "emplace(value) tests for containers with unique keys.\n"; - X x; test::ordered tracker = test::create_ordered(x); @@ -384,8 +475,6 @@ void unique_emplace_tests1(X*, test::random_generator generator) template void equivalent_emplace_tests1(X*, test::random_generator generator) { - std::cerr << "emplace(value) tests for containers with equivalent keys.\n"; - X x; test::ordered tracker = test::create_ordered(x); @@ -414,9 +503,6 @@ void equivalent_emplace_tests1(X*, test::random_generator generator) template void move_emplace_tests(X*, test::random_generator generator) { - std::cerr - << "emplace(move(value)) tests for containers with unique keys.\n"; - X x; test::ordered tracker = test::create_ordered(x); @@ -444,8 +530,7 @@ template void move_emplace_tests(X*, test::random_generator generator) template void default_emplace_tests(X*, test::random_generator) { -#if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100)) - std::cerr << "emplace() tests.\n"; +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 bool is_unique = test::has_unique_keys::value; X x; @@ -480,8 +565,6 @@ template void default_emplace_tests(X*, test::random_generator) template void map_tests(X*, test::random_generator generator) { - std::cerr << "map tests.\n"; - X x; test::ordered tracker = test::create_ordered(x); @@ -508,8 +591,8 @@ template void map_tests(X*, test::random_generator generator) template void map_tests2(X*, test::random_generator generator) { typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; - std::cerr << "insert_or_assign\n"; + UNORDERED_SUB_TEST("insert_or_assign") { test::check_instances check_; @@ -540,8 +623,7 @@ template void map_tests2(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "insert_or_assign(begin)\n"; - + UNORDERED_SUB_TEST("insert_or_assign(begin)") { test::check_instances check_; @@ -571,8 +653,7 @@ template void map_tests2(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "insert_or_assign(end)\n"; - + UNORDERED_SUB_TEST("insert_or_assign(end)") { test::check_instances check_; @@ -602,8 +683,7 @@ template void map_tests2(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "insert_or_assign(last)\n"; - + UNORDERED_SUB_TEST("insert_or_assign(last)") { test::check_instances check_; @@ -639,10 +719,9 @@ template void map_tests2(X*, test::random_generator generator) template void try_emplace_tests(X*, test::random_generator generator) { - std::cerr << "try_emplace(key, value)\n"; - typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; + UNORDERED_SUB_TEST("try_emplace(key, value)") { test::check_instances check_; @@ -682,10 +761,9 @@ template void try_emplace_tests(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "try_emplace(begin(), key, value)\n"; - typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; + UNORDERED_SUB_TEST("try_emplace(begin(), key, value)") { test::check_instances check_; @@ -722,10 +800,9 @@ template void try_emplace_tests(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "try_emplace(end(), key, value)\n"; - typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; + UNORDERED_SUB_TEST("try_emplace(end(), key, value)") { test::check_instances check_; @@ -762,10 +839,9 @@ template void try_emplace_tests(X*, test::random_generator generator) test::check_equivalent_keys(x); } - std::cerr << "try_emplace(pos, key, value)\n"; - typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; + UNORDERED_SUB_TEST("try_emplace(pos, key, value)") { test::check_instances check_; @@ -808,8 +884,6 @@ template void try_emplace_tests(X*, test::random_generator generator) template void map_insert_range_test1(X*, test::random_generator generator) { - std::cerr << "map_insert_range_test1\n"; - test::check_instances check_; typedef test::list void map_insert_range_test2(X*, test::random_generator generator) { - std::cerr << "map_insert_range_test2\n"; - test::check_instances check_; typedef test::list 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()); +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 + 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_UNORDERED_SUN_WORKAROUNDS1 + 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) @@ -1052,7 +1146,7 @@ UNORDERED_AUTO_TEST(set_emplace_test) boost::unordered_set x; overloaded_constructor check; -#if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100)) +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 x.emplace(); BOOST_TEST(x.find(check) != x.end() && *x.find(check) == check); #endif @@ -1098,48 +1192,109 @@ 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)); + + x.clear(); + + x.try_emplace(x.begin(), overloaded_constructor()); + BOOST_TEST(x.find(overloaded_constructor()) != x.end() && + x.find(overloaded_constructor())->second == + overloaded_constructor()); + + x.try_emplace(x.end(), 1); + BOOST_TEST(x.find(overloaded_constructor(1)) != x.end() && + x.find(overloaded_constructor(1))->second == + overloaded_constructor()); + + x.try_emplace(x.begin(), 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 +1313,171 @@ 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); } + +// Use the preprocessor to generate tests using different combinations of +// boost/std piecewise_construct_t/tuple. + +#define PIECEWISE_TEST_NAME boost_tuple_piecewise_tests +#define PIECEWISE_NAMESPACE boost::unordered +#define TUPLE_NAMESPACE boost +#define EMULATING_PIECEWISE_CONSTRUCTION 1 +#include "./insert_tests.cpp" + +#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT + +#define PIECEWISE_TEST_NAME boost_tuple_std_piecewise_tests +#define PIECEWISE_NAMESPACE std +#define TUPLE_NAMESPACE boost +#define EMULATING_PIECEWISE_CONSTRUCTION 1 +#include "./insert_tests.cpp" + +#endif + +#if !defined(BOOST_NO_CXX11_HDR_TUPLE) + +#define PIECEWISE_TEST_NAME std_tuple_boost_piecewise_tests +#define PIECEWISE_NAMESPACE boost::unordered +#define TUPLE_NAMESPACE std +#define EMULATING_PIECEWISE_CONSTRUCTION 0 +#include "./insert_tests.cpp" + +#endif + +#if !defined(BOOST_NO_CXX11_HDR_TUPLE) && \ + BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT + +#define PIECEWISE_TEST_NAME std_piecewise_tests +#define PIECEWISE_NAMESPACE std +#define TUPLE_NAMESPACE std +#define EMULATING_PIECEWISE_CONSTRUCTION 0 +#include "./insert_tests.cpp" + +#endif } -RUN_TESTS() +RUN_TESTS_QUIET() + +#else // PIECEWISE_TEST_NAME + +UNORDERED_AUTO_TEST(PIECEWISE_TEST_NAME) +{ +#if EMULATING_PIECEWISE_CONSTRUCTION + test::detail::disable_construction_tracking _scoped; +#endif + + { + boost::unordered_map, + std::equal_to, + test::allocator1 > > + x; + + x.emplace(PIECEWISE_NAMESPACE::piecewise_construct, + TUPLE_NAMESPACE::make_tuple(), TUPLE_NAMESPACE::make_tuple()); + BOOST_TEST(x.find(overloaded_constructor()) != x.end() && + x.find(overloaded_constructor())->second == + overloaded_constructor()); + + x.emplace(convertible_to_piecewise(), TUPLE_NAMESPACE::make_tuple(1), + TUPLE_NAMESPACE::make_tuple()); + BOOST_TEST(x.find(overloaded_constructor(1)) != x.end() && + x.find(overloaded_constructor(1))->second == + overloaded_constructor()); + + x.emplace(piecewise_rvalue(), TUPLE_NAMESPACE::make_tuple(2, 3), + TUPLE_NAMESPACE::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, TUPLE_NAMESPACE::make_tuple(9, 3, 1), + TUPLE_NAMESPACE::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(PIECEWISE_NAMESPACE::piecewise_construct, + TUPLE_NAMESPACE::make_tuple(), TUPLE_NAMESPACE::make_tuple()); + BOOST_TEST(x.find(overloaded_constructor()) != x.end() && + x.find(overloaded_constructor())->second == + overloaded_constructor()); + + x.emplace(convertible_to_piecewise(), TUPLE_NAMESPACE::make_tuple(1), + TUPLE_NAMESPACE::make_tuple()); + BOOST_TEST(x.find(overloaded_constructor(1)) != x.end() && + x.find(overloaded_constructor(1))->second == + overloaded_constructor()); + + x.emplace(piecewise_rvalue(), TUPLE_NAMESPACE::make_tuple(2, 3), + TUPLE_NAMESPACE::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, TUPLE_NAMESPACE::make_tuple(9, 3, 1), + TUPLE_NAMESPACE::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(BOOST_PP_CAT(PIECEWISE_TEST_NAME, 2)) +{ +#if EMULATING_PIECEWISE_CONSTRUCTION + test::detail::disable_construction_tracking _scoped; +#endif + + boost::unordered_set< + std::pair > + x; + std::pair check; + + x.emplace(PIECEWISE_NAMESPACE::piecewise_construct, + TUPLE_NAMESPACE::make_tuple(), TUPLE_NAMESPACE::make_tuple()); + BOOST_TEST(x.find(check) != x.end() && *x.find(check) == check); + + x.clear(); + x.emplace(PIECEWISE_NAMESPACE::piecewise_construct, + TUPLE_NAMESPACE::make_tuple(1), TUPLE_NAMESPACE::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); +} + +#undef PIECEWISE_TEST_NAME +#undef PIECEWISE_NAMESPACE +#undef TUPLE_NAMESPACE +#undef EMULATING_PIECEWISE_CONSTRUCTION + +#endif diff --git a/test/unordered/merge_tests.cpp b/test/unordered/merge_tests.cpp index 15755ab8..8d0d4915 100644 --- a/test/unordered/merge_tests.cpp +++ b/test/unordered/merge_tests.cpp @@ -99,7 +99,6 @@ UNORDERED_AUTO_TEST(merge_multiset) test::check_equivalent_keys(y); } -#if BOOST_UNORDERED_INTEROPERABLE_NODES UNORDERED_AUTO_TEST(merge_set_and_multiset) { boost::unordered_set x; @@ -139,14 +138,15 @@ UNORDERED_AUTO_TEST(merge_set_and_multiset) test::check_equivalent_keys(x); test::check_equivalent_keys(y); } -#endif -template void merge_empty_test(X*, test::random_generator generator) +template +void merge_empty_test(X1*, X2*, test::random_generator generator) { test::check_instances check_; - test::random_values v(1000, generator); - X x1(v.begin(), v.end()), x2; + test::random_values v(1000, generator); + X1 x1(v.begin(), v.end()); + X2 x2; x1.merge(x2); test::check_container(x1, v); BOOST_TEST(x2.empty()); @@ -160,7 +160,8 @@ void merge_into_empty_test(X*, test::random_generator generator) test::check_instances check_; test::random_values v(1000, generator); - X x1, x2(v.begin(), v.end()); + X x1; + X x2(v.begin(), v.end()); x1.merge(x2); test::check_container(x1, v); BOOST_TEST(x2.empty()); @@ -168,89 +169,173 @@ void merge_into_empty_test(X*, test::random_generator generator) test::check_equivalent_keys(x2); } -template void unique_merge_test(X*, test::random_generator generator) +template +void merge_into_unique_keys_test(X1*, X2*, int hash_equal1, int hash_equal2, + test::random_generator generator) { test::check_instances check_; - test::random_values v1(1000, generator); - test::random_values v2(1000, generator); + test::random_values v1(1000, generator); + test::random_values v2(1000, generator); v1.insert(v2.begin(), boost::next(v2.begin(), 100)); v2.insert(v1.begin(), boost::next(v1.begin(), 100)); - X x1(v1.begin(), v1.end()), x2(v2.begin(), v2.end()); - x1.merge(x2); + X1 x1(v1.begin(), v1.end(), 0, test::hash(hash_equal1), + test::equal_to(hash_equal1)); + X2 x2(v2.begin(), v2.end(), 0, test::hash(hash_equal2), + test::equal_to(hash_equal2)); - test::ordered tracker1 = test::create_ordered(x1); - test::ordered tracker2 = test::create_ordered(x2); - test::ordered tracker_tmp = test::create_ordered(x2); + test::ordered tracker1 = test::create_ordered(x1); + test::ordered tracker2 = test::create_ordered(x2); tracker1.insert(v1.begin(), v1.end()); - tracker_tmp.insert(v2.begin(), v2.end()); - for (BOOST_DEDUCED_TYPENAME test::ordered::iterator it = - tracker_tmp.begin(); - it != tracker_tmp.end(); ++it) { + for (typename X2::iterator it = x2.begin(); it != x2.end(); ++it) { if (!tracker1.insert(*it).second) { tracker2.insert(*it); } } + x1.merge(x2); + tracker1.compare(x1); tracker2.compare(x2); test::check_equivalent_keys(x1); test::check_equivalent_keys(x2); } -template void equiv_merge_test(X*, test::random_generator generator) +template +void merge_into_equiv_keys_test(X1*, X2*, int hash_equal1, int hash_equal2, + test::random_generator generator) { test::check_instances check_; - test::random_values v1(1000, generator); - test::random_values v2(1000, generator); + test::random_values v1(1000, generator); + test::random_values v2(1000, generator); v1.insert(v2.begin(), boost::next(v2.begin(), 100)); v2.insert(v1.begin(), boost::next(v1.begin(), 100)); - X x1(v1.begin(), v1.end()), x2(v2.begin(), v2.end()); + X1 x1(v1.begin(), v1.end(), 0, test::hash(hash_equal1), + test::equal_to(hash_equal1)); + X2 x2(v2.begin(), v2.end(), 0, test::hash(hash_equal2), + test::equal_to(hash_equal2)); x1.merge(x2); - test::ordered tracker1 = test::create_ordered(x1); + test::ordered tracker1 = test::create_ordered(x1); + test::ordered tracker2 = test::create_ordered(x2); tracker1.insert(v1.begin(), v1.end()); - tracker1.insert(v2.begin(), v2.end()); + tracker2.insert(v2.begin(), v2.end()); + tracker1.insert(tracker2.begin(), tracker2.end()); + tracker2.clear(); tracker1.compare(x1); - BOOST_TEST(x2.empty()); + tracker2.compare(x2); test::check_equivalent_keys(x1); test::check_equivalent_keys(x2); } boost::unordered_set >* test_set_std_alloc; +boost::unordered_multiset >* test_multiset_std_alloc; + +boost::unordered_map >* test_map_std_alloc; boost::unordered_multimap >* test_multimap_std_alloc; boost::unordered_set >* test_set; -boost::unordered_multiset >* test_multiset; +boost::unordered_multiset >* test_multiset; + boost::unordered_map >* test_map; -boost::unordered_multimap >* test_multimap; +boost::unordered_multimap >* test_multimap; using test::default_generator; using test::generate_collisions; +// clang-format off UNORDERED_TEST(merge_empty_test, - ((test_set_std_alloc)(test_multimap_std_alloc)(test_set)(test_multiset)( - test_map)(test_multimap))((default_generator)(generate_collisions))) + ((test_set_std_alloc)(test_multiset_std_alloc)) + ((test_set_std_alloc)(test_multiset_std_alloc)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_empty_test, + ((test_map_std_alloc)(test_multimap_std_alloc)) + ((test_map_std_alloc)(test_multimap_std_alloc)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_empty_test, + ((test_set)(test_multiset)) + ((test_set)(test_multiset)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_empty_test, + ((test_map)(test_multimap)) + ((test_map)(test_multimap)) + ((default_generator)(generate_collisions))) UNORDERED_TEST(merge_into_empty_test, - ((test_set_std_alloc)(test_multimap_std_alloc)(test_set)(test_multiset)( - test_map)(test_multimap))((default_generator)(generate_collisions))) + ((test_set_std_alloc)(test_multiset_std_alloc)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_empty_test, + ((test_map_std_alloc)(test_multimap_std_alloc)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_empty_test, + ((test_set)(test_multiset)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_empty_test, + ((test_map)(test_multimap)) + ((default_generator)(generate_collisions))) -UNORDERED_TEST(unique_merge_test, - ((test_set_std_alloc)(test_set)(test_map))((default_generator))) +UNORDERED_TEST(merge_into_unique_keys_test, + ((test_set_std_alloc)) + ((test_set_std_alloc)(test_multiset_std_alloc)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_unique_keys_test, + ((test_map_std_alloc)) + ((test_map_std_alloc)(test_multimap_std_alloc)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_unique_keys_test, + ((test_set)) + ((test_set)(test_multiset)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_unique_keys_test, + ((test_map)) + ((test_map)(test_multimap)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(generate_collisions))) -UNORDERED_TEST(equiv_merge_test, ((test_multimap_std_alloc)(test_multiset)( - test_multimap))((default_generator))) +UNORDERED_TEST(merge_into_equiv_keys_test, + ((test_multiset_std_alloc)) + ((test_set_std_alloc)(test_multiset_std_alloc)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_equiv_keys_test, + ((test_multimap_std_alloc)) + ((test_map_std_alloc)(test_multimap_std_alloc)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_equiv_keys_test, + ((test_multiset)) + ((test_set)(test_multiset)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_equiv_keys_test, + ((test_multimap)) + ((test_map)(test_multimap)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(generate_collisions))) +// clang-format on } RUN_TESTS() diff --git a/test/unordered/noexcept_tests.cpp b/test/unordered/noexcept_tests.cpp index cadf8b70..d801ae1c 100644 --- a/test/unordered/noexcept_tests.cpp +++ b/test/unordered/noexcept_tests.cpp @@ -36,7 +36,8 @@ bool throwing_test_exception = false; void test_throw(char const* name) { if (throwing_test_exception) { - std::cerr << "Throw exception in: " << name << std::endl; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Throw exception in: " << name + << std::endl; throw test_exception(); } } diff --git a/test/unordered/simple_tests.cpp b/test/unordered/simple_tests.cpp index 05d5523d..40ba5fc3 100644 --- a/test/unordered/simple_tests.cpp +++ b/test/unordered/simple_tests.cpp @@ -93,7 +93,7 @@ UNORDERED_AUTO_TEST(simple_tests) using namespace std; srand(14878); - std::cout << "Test unordered_set.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n"; boost::unordered_set set; simple_test(set); @@ -102,7 +102,7 @@ UNORDERED_AUTO_TEST(simple_tests) set.insert(1456); simple_test(set); - std::cout << "Test unordered_multiset.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n"; boost::unordered_multiset multiset; simple_test(multiset); @@ -113,7 +113,7 @@ UNORDERED_AUTO_TEST(simple_tests) } simple_test(multiset); - std::cout << "Test unordered_map.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n"; boost::unordered_map map; for (int i2 = 0; i2 < 1000; ++i2) { @@ -121,7 +121,7 @@ UNORDERED_AUTO_TEST(simple_tests) } simple_test(map); - std::cout << "Test unordered_multimap.\n"; + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n"; boost::unordered_multimap multimap; for (int i3 = 0; i3 < 1000; ++i3) { diff --git a/test/unordered/unnecessary_copy_tests.cpp b/test/unordered/unnecessary_copy_tests.cpp index 7d17dfcf..1e22dd66 100644 --- a/test/unordered/unnecessary_copy_tests.cpp +++ b/test/unordered/unnecessary_copy_tests.cpp @@ -10,7 +10,6 @@ #include "../helpers/postfix.hpp" // clang-format on -#include #include "../helpers/test.hpp" namespace unnecessary_copy_tests { @@ -138,32 +137,36 @@ std::size_t hash_value(unnecessary_copy_tests::count_copies const& x) #define COPY_COUNT(n) \ if (::unnecessary_copy_tests::count_copies::copies != n) { \ BOOST_ERROR("Wrong number of copies."); \ - std::cerr << "Number of copies: " \ - << ::unnecessary_copy_tests::count_copies::copies \ - << " expecting: " << n << std::endl; \ + BOOST_LIGHTWEIGHT_TEST_OSTREAM \ + << "Number of copies: " \ + << ::unnecessary_copy_tests::count_copies::copies \ + << " expecting: " << n << std::endl; \ } #define MOVE_COUNT(n) \ if (::unnecessary_copy_tests::count_copies::moves != n) { \ BOOST_ERROR("Wrong number of moves."); \ - std::cerr << "Number of moves: " \ - << ::unnecessary_copy_tests::count_copies::moves \ - << " expecting: " << n << std::endl; \ + BOOST_LIGHTWEIGHT_TEST_OSTREAM \ + << "Number of moves: " \ + << ::unnecessary_copy_tests::count_copies::moves \ + << " expecting: " << n << std::endl; \ } #define COPY_COUNT_RANGE(a, b) \ if (::unnecessary_copy_tests::count_copies::copies < a || \ ::unnecessary_copy_tests::count_copies::copies > b) { \ BOOST_ERROR("Wrong number of copies."); \ - std::cerr << "Number of copies: " \ - << ::unnecessary_copy_tests::count_copies::copies \ - << " expecting: [" << a << ", " << b << "]" << std::endl; \ + BOOST_LIGHTWEIGHT_TEST_OSTREAM \ + << "Number of copies: " \ + << ::unnecessary_copy_tests::count_copies::copies \ + << " expecting: [" << a << ", " << b << "]" << std::endl; \ } #define MOVE_COUNT_RANGE(a, b) \ if (::unnecessary_copy_tests::count_copies::moves < a || \ ::unnecessary_copy_tests::count_copies::moves > b) { \ BOOST_ERROR("Wrong number of moves."); \ - std::cerr << "Number of moves: " \ - << ::unnecessary_copy_tests::count_copies::moves \ - << " expecting: [" << a << ", " << b << "]" << std::endl; \ + BOOST_LIGHTWEIGHT_TEST_OSTREAM \ + << "Number of moves: " \ + << ::unnecessary_copy_tests::count_copies::moves \ + << " expecting: [" << a << ", " << b << "]" << std::endl; \ } #define COPY_COUNT_EXTRA(a, b) COPY_COUNT_RANGE(a, a + b * EXTRA_CONSTRUCT_COST) #define MOVE_COUNT_EXTRA(a, b) MOVE_COUNT_RANGE(a, a + b * EXTRA_CONSTRUCT_COST) @@ -180,6 +183,53 @@ template void unnecessary_copy_insert_test(T*) reset(); x.insert(a); COPY_COUNT(1); + MOVE_COUNT(0); +} + +template void unnecessary_copy_insert_rvalue_set_test(T*) +{ + T x; + BOOST_DEDUCED_TYPENAME T::value_type a; + reset(); + x.insert(boost::move(a)); + COPY_COUNT(0); + MOVE_COUNT(1); + + BOOST_DEDUCED_TYPENAME T::value_type a2; + reset(); + x.insert(boost::move(a)); + COPY_COUNT(0); + MOVE_COUNT((x.size() == 2 ? 1 : 0)); +} + +template void unnecessary_copy_insert_rvalue_map_test(T*) +{ + // Doesn't currently try to emulate std::pair move construction, + // so std::pair's require a copy. Could try emulating it in + // construct_from_args. + + T x; + BOOST_DEDUCED_TYPENAME T::value_type a; + reset(); + x.insert(boost::move(a)); +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + COPY_COUNT(1); + MOVE_COUNT(0); +#else + COPY_COUNT(0); + MOVE_COUNT(1); +#endif + + BOOST_DEDUCED_TYPENAME T::value_type a2; + reset(); + x.insert(boost::move(a)); +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + COPY_COUNT((x.size() == 2 ? 1 : 0)); + MOVE_COUNT(0); +#else + COPY_COUNT(0); + MOVE_COUNT((x.size() == 2 ? 1 : 0)); +#endif } boost::unordered_set* set; @@ -188,6 +238,8 @@ boost::unordered_map* map; boost::unordered_multimap* multimap; UNORDERED_TEST(unnecessary_copy_insert_test, ((set)(multiset)(map)(multimap))) +UNORDERED_TEST(unnecessary_copy_insert_rvalue_set_test, ((set)(multiset))) +UNORDERED_TEST(unnecessary_copy_insert_rvalue_map_test, ((map)(multimap))) template void unnecessary_copy_emplace_test(T*) { @@ -310,20 +362,15 @@ UNORDERED_AUTO_TEST(unnecessary_copy_emplace_set_test) // 0 arguments // -#if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100)) +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 // The container will have to create a copy in order to compare with // the existing element. reset(); x.emplace(); -#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || \ - !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + // source_cost doesn't make much sense here, but it seems to fit. COPY_COUNT(1); MOVE_COUNT(source_cost); -#else - COPY_COUNT(1); - MOVE_COUNT(1 + source_cost); -#endif #endif // @@ -347,13 +394,8 @@ UNORDERED_AUTO_TEST(unnecessary_copy_emplace_set_test) // No move should take place. reset(); x.emplace(boost::move(a)); -#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) COPY_COUNT(0); MOVE_COUNT(0); -#else - COPY_COUNT(0); - MOVE_COUNT(1); -#endif // Use a new value for cases where a did get moved... count_copies b; @@ -409,7 +451,7 @@ UNORDERED_AUTO_TEST(unnecessary_copy_emplace_map_test) // 0 arguments // -#if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100)) +#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 // COPY_COUNT(1) would be okay here. reset(); x.emplace(); @@ -501,7 +543,7 @@ UNORDERED_AUTO_TEST(unnecessary_copy_emplace_map_test) COPY_COUNT(0); MOVE_COUNT(0); -#if !defined(BOOST_NO_CXX11_HDR_TUPLE) || defined(BOOST_HAS_TR1_TUPLE) +#if BOOST_UNORDERED_TUPLE_ARGS reset(); x.emplace(boost::unordered::piecewise_construct,