From 5854090dc79d73657fe2589eec73af0171c452b3 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Fri, 5 Jan 2018 17:48:13 +0000 Subject: [PATCH] Swap noexcept support Not properly supported as we don't have is_nothrow_swappable yet. --- .../boost/unordered/detail/implementation.hpp | 53 +++++++++++++++++-- include/boost/unordered/unordered_map.hpp | 32 +++++------ include/boost/unordered/unordered_set.hpp | 32 +++++------ test/unordered/noexcept_tests.cpp | 50 +++++++++++++++++ 4 files changed, 127 insertions(+), 40 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 5644773e..a44a2c27 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -31,12 +31,15 @@ #include #include #include +#include #include #include +#include #include #include #include #include +#include #include #include #include @@ -751,6 +754,19 @@ namespace boost { #pragma warning(pop) #endif + //////////////////////////////////////////////////////////////////////////// + // is_nothrow_swappable + // + // TODO: Replace this very basic implementation when the full type_traits + // implementation is available. + + template + struct is_nothrow_swappable + : boost::integral_constant::value && !boost::is_const::value> + { + }; + ////////////////////////////////////////////////////////////////////////// // value_base // @@ -2756,6 +2772,9 @@ namespace boost { static const bool nothrow_move_constructible = boost::is_nothrow_move_constructible::value && boost::is_nothrow_move_constructible

::value; + static const bool nothrow_swappable = + boost::unordered::detail::is_nothrow_swappable::value && + boost::unordered::detail::is_nothrow_swappable

::value; private: friend class boost::unordered::detail::set_hash_functions::propagate_on_container_swap::value>()); + + boost::swap(buckets_, x.buckets_); + boost::swap(bucket_count_, x.bucket_count_); + boost::swap(size_, x.size_); + std::swap(mlf_, x.mlf_); + std::swap(max_load_, x.max_load_); + this->current_functions().swap(x.current_functions()); + } + + // Only swaps the allocators if propagate_on_container_swap. + // If not propagate_on_container_swap and allocators aren't + // equal, behaviour is undefined. + void swap(table& x) + { + BOOST_ASSERT(allocator_traits< + node_allocator>::propagate_on_container_swap::value || + node_alloc() == x.node_alloc()); + swap(x, boost::unordered::detail::integral_constant()); + } + // Only call with nodes allocated with the currect allocator, or // one that is equal to it. (Can't assert because other's // allocators might have already been moved). diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 06f2bb1a..58d18944 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -715,11 +715,10 @@ namespace boost { BOOST_UNORDERED_DEPRECATED("Use erase instead") void erase_return_void(const_iterator it) { erase(it); } - 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 swap(unordered_map&) + BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&& + boost::unordered::detail::is_nothrow_swappable::value&& + boost::unordered::detail::is_nothrow_swappable

::value); void clear() BOOST_NOEXCEPT { table_.clear_impl(); } template @@ -1327,11 +1326,10 @@ namespace boost { BOOST_UNORDERED_DEPRECATED("Use erase instead") void erase_return_void(const_iterator it) { erase(it); } - 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 swap(unordered_multimap&) + BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&& + boost::unordered::detail::is_nothrow_swappable::value&& + boost::unordered::detail::is_nothrow_swappable

::value); void clear() BOOST_NOEXCEPT { table_.clear_impl(); } template @@ -1740,10 +1738,9 @@ namespace boost { 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

) + BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&& + boost::unordered::detail::is_nothrow_swappable::value&& + boost::unordered::detail::is_nothrow_swappable

::value) { table_.swap(other.table_); } @@ -2217,10 +2214,9 @@ namespace boost { 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

) + BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&& + boost::unordered::detail::is_nothrow_swappable::value&& + boost::unordered::detail::is_nothrow_swappable

::value) { table_.swap(other.table_); } diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 8f991660..e119ee76 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -441,11 +441,10 @@ namespace boost { BOOST_UNORDERED_DEPRECATED("Use erase instead") void erase_return_void(const_iterator it) { erase(it); } - 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 swap(unordered_set&) + BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&& + boost::unordered::detail::is_nothrow_swappable::value&& + boost::unordered::detail::is_nothrow_swappable

::value); void clear() BOOST_NOEXCEPT { table_.clear_impl(); } template @@ -991,11 +990,10 @@ namespace boost { BOOST_UNORDERED_DEPRECATED("Use erase instead") void erase_return_void(const_iterator it) { erase(it); } - 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 swap(unordered_multiset&) + BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&& + boost::unordered::detail::is_nothrow_swappable::value&& + boost::unordered::detail::is_nothrow_swappable

::value); void clear() BOOST_NOEXCEPT { table_.clear_impl(); } template @@ -1365,10 +1363,9 @@ namespace boost { 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

) + BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&& + boost::unordered::detail::is_nothrow_swappable::value&& + boost::unordered::detail::is_nothrow_swappable

::value) { table_.swap(other.table_); } @@ -1760,10 +1757,9 @@ namespace boost { 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

) + BOOST_NOEXCEPT_IF(value_allocator_traits::is_always_equal::value&& + boost::unordered::detail::is_nothrow_swappable::value&& + boost::unordered::detail::is_nothrow_swappable

::value) { table_.swap(other.table_); } diff --git a/test/unordered/noexcept_tests.cpp b/test/unordered/noexcept_tests.cpp index 29c805a2..f5a38a19 100644 --- a/test/unordered/noexcept_tests.cpp +++ b/test/unordered/noexcept_tests.cpp @@ -89,6 +89,7 @@ namespace noexcept_tests { }; typedef hash_nothrow hash_nothrow_move; + typedef hash_nothrow hash_nothrow_swap; template @@ -137,14 +138,18 @@ namespace noexcept_tests { }; typedef equal_to_nothrow equal_to_nothrow_move; + typedef equal_to_nothrow equal_to_nothrow_swap; bool have_is_nothrow_move = false; + bool have_is_nothrow_swap = false; UNORDERED_AUTO_TEST (check_is_nothrow_move) { BOOST_TEST( !boost::is_nothrow_move_constructible::value); have_is_nothrow_move = boost::is_nothrow_move_constructible::value; + have_is_nothrow_swap = + boost::unordered::detail::is_nothrow_swappable::value; // Copied from boost::is_nothrow_move_constructible implementation // to make sure this does actually detect it when expected. @@ -156,6 +161,17 @@ namespace noexcept_tests { !BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40800) BOOST_TEST(have_is_nothrow_move); #endif + +#if !defined(BOOST_NO_SFINAE_EXPR) && !defined(BOOST_NO_CXX11_NOEXCEPT) && \ + !defined(BOOST_NO_CXX11_DECLTYPE) && \ + !defined(BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS) +// TODO: Turn test on when unordered starts to use is_nothrow_swap +// BOOST_TEST(have_is_nothrow_swap); +#endif + + BOOST_LIGHTWEIGHT_TEST_OSTREAM + << "have_is_nothrow_move: " << have_is_nothrow_move << std::endl + << "have_is_nothrow_swap: " << have_is_nothrow_swap << std::endl; } UNORDERED_AUTO_TEST (test_noexcept) { @@ -203,6 +219,40 @@ namespace noexcept_tests { throwing_test_exception = false; } } + + UNORDERED_AUTO_TEST (test_nothrow_swap_when_noexcept) { + typedef boost::unordered_set + throwing_set; + + if (have_is_nothrow_swap) { + BOOST_TEST( + boost::unordered::detail::is_nothrow_swappable::value); + } + + throwing_test_exception = false; + + throwing_set x1; + throwing_set x2; + x1.insert(10); + x1.insert(50); + for (int i = 0; i < 100; ++i) { + x2.insert(i); + } + + try { + throwing_test_exception = true; + + x1.swap(x2); + BOOST_TEST(x1.size() == 100); + BOOST_TEST(x2.size() == 2); + BOOST_TEST(*x2.begin() == 10 || *x2.begin() == 50); + BOOST_TEST(have_is_nothrow_swap); + } catch (test_exception) { + BOOST_TEST(!have_is_nothrow_swap); + } + + throwing_test_exception = false; + } } RUN_TESTS()