// Copyright 2013 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" #if defined(BOOST_MSVC) #pragma warning(push) // conditional expression is constant #pragma warning(disable : 4127) #endif namespace noexcept_tests { // Test the noexcept is set correctly for the move constructor. struct hash_possible_exception : boost::hash { hash_possible_exception(hash_possible_exception const&) {} hash_possible_exception& operator=(hash_possible_exception const&) { return *this; } }; struct equal_to_possible_exception : std::equal_to { equal_to_possible_exception(equal_to_possible_exception const&) {} equal_to_possible_exception& operator=(equal_to_possible_exception const&) { return *this; } }; // Test that the move constructor does actually move without throwing // an exception when it claims to. struct test_exception { }; bool throwing_test_exception = false; void test_throw(char const* name) { if (throwing_test_exception) { BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Throw exception in: " << name << std::endl; throw test_exception(); } } template class hash_nothrow : boost::hash { BOOST_COPYABLE_AND_MOVABLE(hash_nothrow) typedef boost::hash base; public: hash_nothrow(BOOST_RV_REF(hash_nothrow)) BOOST_NOEXCEPT_IF(nothrow_move_construct) { if (!nothrow_move_construct) { test_throw("Move Constructor"); } } hash_nothrow() { test_throw("Constructor"); } hash_nothrow(hash_nothrow const&) { test_throw("Copy"); } hash_nothrow& operator=(BOOST_COPY_ASSIGN_REF(hash_nothrow)) { test_throw("Assign"); return *this; } hash_nothrow& operator=(BOOST_RV_REF(hash_nothrow)) BOOST_NOEXCEPT_IF(nothrow_move_assign) { if (!nothrow_move_assign) { test_throw("Move Assign"); } return *this; } std::size_t operator()(int x) const { test_throw("Operator"); return static_cast(*this)(x); } friend void swap(hash_nothrow&, hash_nothrow&) BOOST_NOEXCEPT_IF(nothrow_swap) { if (!nothrow_swap) { test_throw("Swap"); } } }; typedef hash_nothrow hash_nothrow_move_construct; typedef hash_nothrow hash_nothrow_move_assign; typedef hash_nothrow hash_nothrow_swap; template class equal_to_nothrow { BOOST_COPYABLE_AND_MOVABLE(equal_to_nothrow) typedef boost::hash base; public: equal_to_nothrow(BOOST_RV_REF(equal_to_nothrow)) BOOST_NOEXCEPT_IF(nothrow_move_construct) { if (!nothrow_move_construct) { test_throw("Move Constructor"); } } equal_to_nothrow() { test_throw("Constructor"); } equal_to_nothrow(equal_to_nothrow const&) { test_throw("Copy"); } equal_to_nothrow& operator=(BOOST_COPY_ASSIGN_REF(equal_to_nothrow)) { test_throw("Assign"); return *this; } equal_to_nothrow& operator=(BOOST_RV_REF(equal_to_nothrow)) BOOST_NOEXCEPT_IF(nothrow_move_assign) { if (!nothrow_move_assign) { test_throw("Move Assign"); } return *this; } std::size_t operator()(int x, int y) const { test_throw("Operator"); return x == y; } friend void swap(equal_to_nothrow&, equal_to_nothrow&) BOOST_NOEXCEPT_IF(nothrow_swap) { if (!nothrow_swap) { test_throw("Swap"); } } }; typedef equal_to_nothrow equal_to_nothrow_move_construct; typedef equal_to_nothrow equal_to_nothrow_move_assign; typedef equal_to_nothrow equal_to_nothrow_swap; bool have_is_nothrow_move = false; bool have_is_nothrow_move_assign = false; bool have_is_nothrow_swap = false; UNORDERED_AUTO_TEST (check_is_nothrow_move) { BOOST_TEST( !boost::is_nothrow_move_constructible::value); BOOST_TEST( !boost::is_nothrow_move_assignable::value); BOOST_TEST(!boost::unordered::detail::is_nothrow_swappable< hash_possible_exception>::value); BOOST_TEST((!boost::is_nothrow_move_constructible< equal_to_nothrow >::value)); BOOST_TEST((!boost::is_nothrow_move_assignable< equal_to_nothrow >::value)); BOOST_TEST((!boost::unordered::detail::is_nothrow_swappable< equal_to_nothrow >::value)); have_is_nothrow_move = boost::is_nothrow_move_constructible::value; have_is_nothrow_move_assign = boost::is_nothrow_move_assignable::value; have_is_nothrow_swap = boost::unordered::detail::is_nothrow_swappable::value; // Check that the traits work when expected. #if !defined(BOOST_NO_CXX11_NOEXCEPT) && !defined(BOOST_NO_SFINAE_EXPR) && \ !BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40800) BOOST_TEST(have_is_nothrow_move); BOOST_TEST(have_is_nothrow_move_assign); #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) { if (have_is_nothrow_move) { BOOST_TEST((boost::is_nothrow_move_constructible< boost::unordered_set >::value)); BOOST_TEST((boost::is_nothrow_move_constructible< boost::unordered_multiset >::value)); BOOST_TEST((boost::is_nothrow_move_constructible< boost::unordered_map >::value)); BOOST_TEST((boost::is_nothrow_move_constructible< boost::unordered_multimap >::value)); } BOOST_TEST((!boost::is_nothrow_move_constructible< boost::unordered_set >::value)); BOOST_TEST( (!boost::is_nothrow_move_constructible, equal_to_possible_exception> >::value)); } UNORDERED_AUTO_TEST (test_nothrow_move_when_noexcept) { typedef boost::unordered_set throwing_set; if (have_is_nothrow_move) { BOOST_TEST(boost::is_nothrow_move_constructible::value); } throwing_test_exception = false; throwing_set x1; x1.insert(10); x1.insert(50); try { throwing_test_exception = true; throwing_set x2 = boost::move(x1); BOOST_TEST(x2.size() == 2); BOOST_TEST(*x2.begin() == 10 || *x2.begin() == 50); BOOST_TEST(have_is_nothrow_move); } catch (test_exception) { BOOST_TEST(!have_is_nothrow_move); } throwing_test_exception = false; } UNORDERED_AUTO_TEST (test_nothrow_move_assign_when_noexcept) { typedef boost::unordered_set throwing_set; if (have_is_nothrow_move_assign) { BOOST_TEST(boost::is_nothrow_move_assignable::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; x2 = boost::move(x1); BOOST_TEST(x2.size() == 2); BOOST_TEST(*x2.begin() == 10 || *x2.begin() == 50); BOOST_TEST(have_is_nothrow_move_assign); } catch (test_exception) { BOOST_TEST(!have_is_nothrow_move_assign); } 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; } } #if defined(BOOST_MSVC) #pragma warning(pop) #endif RUN_TESTS()