1 2 // Copyright 2013 Daniel James. 3 // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 6 // clang-format off 7 #include "../helpers/prefix.hpp" 8 #include <boost/unordered_set.hpp> 9 #include <boost/unordered_map.hpp> 10 #include "../helpers/postfix.hpp" 11 // clang-format on 12 13 #include "../helpers/test.hpp" 14 15 #if defined(BOOST_MSVC) 16 #pragma warning(push) 17 // conditional expression is constant 18 #pragma warning(disable : 4127) 19 #endif 20 21 namespace noexcept_tests { 22 // Test the noexcept is set correctly for the move constructor. 23 24 struct hash_possible_exception : boost::hash<int> 25 { hash_possible_exceptionnoexcept_tests::hash_possible_exception26 hash_possible_exception(hash_possible_exception const&) {} operator =noexcept_tests::hash_possible_exception27 hash_possible_exception& operator=(hash_possible_exception const&) 28 { 29 return *this; 30 } 31 }; 32 33 struct equal_to_possible_exception : std::equal_to<int> 34 { equal_to_possible_exceptionnoexcept_tests::equal_to_possible_exception35 equal_to_possible_exception(equal_to_possible_exception const&) {} operator =noexcept_tests::equal_to_possible_exception36 equal_to_possible_exception& operator=(equal_to_possible_exception const&) 37 { 38 return *this; 39 } 40 }; 41 42 // Test that the move constructor does actually move without throwing 43 // an exception when it claims to. 44 45 struct test_exception 46 { 47 }; 48 49 bool throwing_test_exception = false; test_throw(char const * name)50 void test_throw(char const* name) 51 { 52 if (throwing_test_exception) { 53 BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Throw exception in: " << name 54 << std::endl; 55 throw test_exception(); 56 } 57 } 58 59 template <bool nothrow_move_construct, bool nothrow_move_assign, 60 bool nothrow_swap> 61 class hash_nothrow : boost::hash<int> 62 { 63 BOOST_COPYABLE_AND_MOVABLE(hash_nothrow) 64 65 typedef boost::hash<int> base; 66 67 public: 68 hash_nothrow(BOOST_RV_REF(hash_nothrow)) BOOST_NOEXCEPT_IF(nothrow_move_construct)69 BOOST_NOEXCEPT_IF(nothrow_move_construct) 70 { 71 if (!nothrow_move_construct) { 72 test_throw("Move Constructor"); 73 } 74 } 75 hash_nothrow()76 hash_nothrow() { test_throw("Constructor"); } hash_nothrow(hash_nothrow const &)77 hash_nothrow(hash_nothrow const&) { test_throw("Copy"); } operator =(BOOST_COPY_ASSIGN_REF (hash_nothrow))78 hash_nothrow& operator=(BOOST_COPY_ASSIGN_REF(hash_nothrow)) 79 { 80 test_throw("Assign"); 81 return *this; 82 } operator =(BOOST_RV_REF (hash_nothrow))83 hash_nothrow& operator=(BOOST_RV_REF(hash_nothrow)) 84 BOOST_NOEXCEPT_IF(nothrow_move_assign) 85 { 86 if (!nothrow_move_assign) { 87 test_throw("Move Assign"); 88 } 89 return *this; 90 } operator ()(int x) const91 std::size_t operator()(int x) const 92 { 93 test_throw("Operator"); 94 return static_cast<base const&>(*this)(x); 95 } swap(hash_nothrow &,hash_nothrow &)96 friend void swap(hash_nothrow&, hash_nothrow&) 97 BOOST_NOEXCEPT_IF(nothrow_swap) 98 { 99 if (!nothrow_swap) { 100 test_throw("Swap"); 101 } 102 } 103 }; 104 105 typedef hash_nothrow<true, false, false> hash_nothrow_move_construct; 106 typedef hash_nothrow<false, true, false> hash_nothrow_move_assign; 107 typedef hash_nothrow<false, false, true> hash_nothrow_swap; 108 109 template <bool nothrow_move_construct, bool nothrow_move_assign, 110 bool nothrow_swap> 111 class equal_to_nothrow 112 { 113 BOOST_COPYABLE_AND_MOVABLE(equal_to_nothrow) 114 115 typedef boost::hash<int> base; 116 117 public: 118 equal_to_nothrow(BOOST_RV_REF(equal_to_nothrow)) BOOST_NOEXCEPT_IF(nothrow_move_construct)119 BOOST_NOEXCEPT_IF(nothrow_move_construct) 120 { 121 if (!nothrow_move_construct) { 122 test_throw("Move Constructor"); 123 } 124 } 125 equal_to_nothrow()126 equal_to_nothrow() { test_throw("Constructor"); } equal_to_nothrow(equal_to_nothrow const &)127 equal_to_nothrow(equal_to_nothrow const&) { test_throw("Copy"); } operator =(BOOST_COPY_ASSIGN_REF (equal_to_nothrow))128 equal_to_nothrow& operator=(BOOST_COPY_ASSIGN_REF(equal_to_nothrow)) 129 { 130 test_throw("Assign"); 131 return *this; 132 } operator =(BOOST_RV_REF (equal_to_nothrow))133 equal_to_nothrow& operator=(BOOST_RV_REF(equal_to_nothrow)) 134 BOOST_NOEXCEPT_IF(nothrow_move_assign) 135 { 136 if (!nothrow_move_assign) { 137 test_throw("Move Assign"); 138 } 139 return *this; 140 } operator ()(int x,int y) const141 std::size_t operator()(int x, int y) const 142 { 143 test_throw("Operator"); 144 return x == y; 145 } swap(equal_to_nothrow &,equal_to_nothrow &)146 friend void swap(equal_to_nothrow&, equal_to_nothrow&) 147 BOOST_NOEXCEPT_IF(nothrow_swap) 148 { 149 if (!nothrow_swap) { 150 test_throw("Swap"); 151 } 152 } 153 }; 154 155 typedef equal_to_nothrow<true, false, false> equal_to_nothrow_move_construct; 156 typedef equal_to_nothrow<false, true, false> equal_to_nothrow_move_assign; 157 typedef equal_to_nothrow<false, false, true> equal_to_nothrow_swap; 158 159 bool have_is_nothrow_move = false; 160 bool have_is_nothrow_move_assign = false; 161 bool have_is_nothrow_swap = false; 162 UNORDERED_AUTO_TEST(check_is_nothrow_move)163 UNORDERED_AUTO_TEST (check_is_nothrow_move) { 164 BOOST_TEST( 165 !boost::is_nothrow_move_constructible<hash_possible_exception>::value); 166 BOOST_TEST( 167 !boost::is_nothrow_move_assignable<hash_possible_exception>::value); 168 BOOST_TEST(!boost::is_nothrow_swappable<hash_possible_exception>::value); 169 BOOST_TEST((!boost::is_nothrow_move_constructible< 170 equal_to_nothrow<false, false, false> >::value)); 171 BOOST_TEST((!boost::is_nothrow_move_assignable< 172 equal_to_nothrow<false, false, false> >::value)); 173 BOOST_TEST((!boost::is_nothrow_swappable< 174 equal_to_nothrow<false, false, false> >::value)); 175 176 have_is_nothrow_move = 177 boost::is_nothrow_move_constructible<hash_nothrow_move_construct>::value; 178 have_is_nothrow_move_assign = 179 boost::is_nothrow_move_assignable<hash_nothrow_move_assign>::value; 180 have_is_nothrow_swap = 181 boost::is_nothrow_swappable<hash_nothrow_swap>::value; 182 183 // Check that the traits work when expected. 184 #if !defined(BOOST_NO_CXX11_NOEXCEPT) && !defined(BOOST_NO_SFINAE_EXPR) && \ 185 !BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40800) 186 BOOST_TEST(have_is_nothrow_move); 187 BOOST_TEST(have_is_nothrow_move_assign); 188 #endif 189 190 #if !defined(BOOST_NO_SFINAE_EXPR) && !defined(BOOST_NO_CXX11_NOEXCEPT) && \ 191 !defined(BOOST_NO_CXX11_DECLTYPE) && \ 192 !defined(BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS) 193 BOOST_TEST(have_is_nothrow_swap); 194 #endif 195 196 BOOST_LIGHTWEIGHT_TEST_OSTREAM 197 << "have_is_nothrow_move: " << have_is_nothrow_move << std::endl 198 << "have_is_nothrow_swap: " << have_is_nothrow_swap << std::endl; 199 } 200 UNORDERED_AUTO_TEST(test_noexcept)201 UNORDERED_AUTO_TEST (test_noexcept) { 202 if (have_is_nothrow_move) { 203 BOOST_TEST((boost::is_nothrow_move_constructible< 204 boost::unordered_set<int> >::value)); 205 BOOST_TEST((boost::is_nothrow_move_constructible< 206 boost::unordered_multiset<int> >::value)); 207 BOOST_TEST((boost::is_nothrow_move_constructible< 208 boost::unordered_map<int, int> >::value)); 209 BOOST_TEST((boost::is_nothrow_move_constructible< 210 boost::unordered_multimap<int, int> >::value)); 211 } 212 213 BOOST_TEST((!boost::is_nothrow_move_constructible< 214 boost::unordered_set<int, hash_possible_exception> >::value)); 215 BOOST_TEST( 216 (!boost::is_nothrow_move_constructible<boost::unordered_multiset<int, 217 boost::hash<int>, equal_to_possible_exception> >::value)); 218 } 219 UNORDERED_AUTO_TEST(test_nothrow_move_when_noexcept)220 UNORDERED_AUTO_TEST (test_nothrow_move_when_noexcept) { 221 typedef boost::unordered_set<int, hash_nothrow_move_construct, 222 equal_to_nothrow_move_construct> 223 throwing_set; 224 225 if (have_is_nothrow_move) { 226 BOOST_TEST(boost::is_nothrow_move_constructible<throwing_set>::value); 227 } 228 229 throwing_test_exception = false; 230 231 throwing_set x1; 232 x1.insert(10); 233 x1.insert(50); 234 235 try { 236 throwing_test_exception = true; 237 238 throwing_set x2 = boost::move(x1); 239 BOOST_TEST(x2.size() == 2); 240 BOOST_TEST(*x2.begin() == 10 || *x2.begin() == 50); 241 BOOST_TEST(have_is_nothrow_move); 242 } catch (test_exception) { 243 BOOST_TEST(!have_is_nothrow_move); 244 } 245 246 throwing_test_exception = false; 247 } 248 UNORDERED_AUTO_TEST(test_nothrow_move_assign_when_noexcept)249 UNORDERED_AUTO_TEST (test_nothrow_move_assign_when_noexcept) { 250 typedef boost::unordered_set<int, hash_nothrow_move_assign, 251 equal_to_nothrow_move_assign> 252 throwing_set; 253 254 if (have_is_nothrow_move_assign) { 255 BOOST_TEST(boost::is_nothrow_move_assignable<throwing_set>::value); 256 } 257 258 throwing_test_exception = false; 259 260 throwing_set x1; 261 throwing_set x2; 262 x1.insert(10); 263 x1.insert(50); 264 for (int i = 0; i < 100; ++i) { 265 x2.insert(i); 266 } 267 268 try { 269 throwing_test_exception = true; 270 271 x2 = boost::move(x1); 272 BOOST_TEST(x2.size() == 2); 273 BOOST_TEST(*x2.begin() == 10 || *x2.begin() == 50); 274 BOOST_TEST(have_is_nothrow_move_assign); 275 } catch (test_exception) { 276 BOOST_TEST(!have_is_nothrow_move_assign); 277 } 278 279 throwing_test_exception = false; 280 } 281 UNORDERED_AUTO_TEST(test_nothrow_swap_when_noexcept)282 UNORDERED_AUTO_TEST (test_nothrow_swap_when_noexcept) { 283 typedef boost::unordered_set<int, hash_nothrow_swap, equal_to_nothrow_swap> 284 throwing_set; 285 286 if (have_is_nothrow_swap) { 287 BOOST_TEST(boost::is_nothrow_swappable<throwing_set>::value); 288 } 289 290 throwing_test_exception = false; 291 292 throwing_set x1; 293 throwing_set x2; 294 x1.insert(10); 295 x1.insert(50); 296 for (int i = 0; i < 100; ++i) { 297 x2.insert(i); 298 } 299 300 try { 301 throwing_test_exception = true; 302 303 x1.swap(x2); 304 BOOST_TEST(x1.size() == 100); 305 BOOST_TEST(x2.size() == 2); 306 BOOST_TEST(*x2.begin() == 10 || *x2.begin() == 50); 307 BOOST_TEST(have_is_nothrow_swap); 308 } catch (test_exception) { 309 BOOST_TEST(!have_is_nothrow_swap); 310 } 311 312 throwing_test_exception = false; 313 } 314 } 315 316 #if defined(BOOST_MSVC) 317 #pragma warning(pop) 318 #endif 319 320 RUN_TESTS() 321