1
2// Copyright 2013 Daniel James.
3// Copyright 2022-2023 Christian Mazakas.
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6
7#include "../helpers/unordered.hpp"
8
9#include "../helpers/fwd.hpp"
10#include "../helpers/test.hpp"
11
12#if defined(BOOST_MSVC)
13#pragma warning(push)
14// conditional expression is constant
15#pragma warning(disable : 4127)
16#endif
17
18namespace noexcept_tests {
19 // Test the noexcept is set correctly for the move constructor.
20
21 struct hash_possible_exception : boost::hash<int>
22 {
23 hash_possible_exception(hash_possible_exception const&) {}
24 hash_possible_exception& operator=(hash_possible_exception const&)
25 {
26 return *this;
27 }
28 };
29
30 struct equal_to_possible_exception : std::equal_to<int>
31 {
32 equal_to_possible_exception(equal_to_possible_exception const&) {}
33 equal_to_possible_exception& operator=(equal_to_possible_exception const&)
34 {
35 return *this;
36 }
37 };
38
39 // Test that the move constructor does actually move without throwing
40 // an exception when it claims to.
41
42 struct test_exception
43 {
44 };
45
46 bool throwing_test_exception = false;
47 void test_throw(char const* name)
48 {
49 if (throwing_test_exception) {
50 BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Throw exception in: " << name
51 << std::endl;
52 throw test_exception();
53 }
54 }
55
56 template <bool nothrow_move_construct, bool nothrow_move_assign,
57 bool nothrow_swap>
58 class hash_nothrow : boost::hash<int>
59 {
60 typedef boost::hash<int> base;
61
62 public:
63 hash_nothrow(hash_nothrow&&) noexcept(nothrow_move_construct)
64 {
65 if (!nothrow_move_construct) {
66 test_throw(name: "Move Constructor");
67 }
68 }
69
70 hash_nothrow() { test_throw(name: "Constructor"); }
71 hash_nothrow(hash_nothrow const&) { test_throw(name: "Copy"); }
72 hash_nothrow& operator=(hash_nothrow const&)
73 {
74 test_throw(name: "Assign");
75 return *this;
76 }
77 hash_nothrow& operator=(hash_nothrow&&)
78 noexcept(nothrow_move_assign)
79 {
80 if (!nothrow_move_assign) {
81 test_throw(name: "Move Assign");
82 }
83 return *this;
84 }
85 std::size_t operator()(int x) const
86 {
87 test_throw(name: "Operator");
88 return static_cast<base const&>(*this)(x);
89 }
90 friend void swap(hash_nothrow&, hash_nothrow&) noexcept(nothrow_swap)
91 {
92 if (!nothrow_swap) {
93 test_throw(name: "Swap");
94 }
95 }
96 };
97
98 typedef hash_nothrow<true, false, false> hash_nothrow_move_construct;
99 typedef hash_nothrow<false, true, false> hash_nothrow_move_assign;
100 typedef hash_nothrow<false, false, true> hash_nothrow_swap;
101
102 template <bool nothrow_move_construct, bool nothrow_move_assign,
103 bool nothrow_swap>
104 class equal_to_nothrow
105 {
106 typedef boost::hash<int> base;
107
108 public:
109 equal_to_nothrow(equal_to_nothrow&&)
110 noexcept(nothrow_move_construct)
111 {
112 if (!nothrow_move_construct) {
113 test_throw(name: "Move Constructor");
114 }
115 }
116
117 equal_to_nothrow() { test_throw(name: "Constructor"); }
118 equal_to_nothrow(equal_to_nothrow const&) { test_throw(name: "Copy"); }
119 equal_to_nothrow& operator=(equal_to_nothrow const&)
120 {
121 test_throw(name: "Assign");
122 return *this;
123 }
124 equal_to_nothrow& operator=(equal_to_nothrow&&)
125 noexcept(nothrow_move_assign)
126 {
127 if (!nothrow_move_assign) {
128 test_throw(name: "Move Assign");
129 }
130 return *this;
131 }
132 std::size_t operator()(int x, int y) const
133 {
134 test_throw(name: "Operator");
135 return x == y;
136 }
137 friend void swap(equal_to_nothrow&, equal_to_nothrow&)
138 noexcept(nothrow_swap)
139 {
140 if (!nothrow_swap) {
141 test_throw(name: "Swap");
142 }
143 }
144 };
145
146 typedef equal_to_nothrow<true, false, false> equal_to_nothrow_move_construct;
147 typedef equal_to_nothrow<false, true, false> equal_to_nothrow_move_assign;
148 typedef equal_to_nothrow<false, false, true> equal_to_nothrow_swap;
149
150 bool have_is_nothrow_move = false;
151 bool have_is_nothrow_move_assign = false;
152 bool have_is_nothrow_swap = false;
153
154 UNORDERED_AUTO_TEST (check_is_nothrow_move) {
155 BOOST_TEST(
156 !boost::is_nothrow_move_constructible<hash_possible_exception>::value);
157 BOOST_TEST(
158 !boost::is_nothrow_move_assignable<hash_possible_exception>::value);
159 BOOST_TEST(!boost::is_nothrow_swappable<hash_possible_exception>::value);
160 BOOST_TEST((!boost::is_nothrow_move_constructible<
161 equal_to_nothrow<false, false, false> >::value));
162 BOOST_TEST((!boost::is_nothrow_move_assignable<
163 equal_to_nothrow<false, false, false> >::value));
164 BOOST_TEST((!boost::is_nothrow_swappable<
165 equal_to_nothrow<false, false, false> >::value));
166
167 have_is_nothrow_move =
168 boost::is_nothrow_move_constructible<hash_nothrow_move_construct>::value;
169 have_is_nothrow_move_assign =
170 boost::is_nothrow_move_assignable<hash_nothrow_move_assign>::value;
171 have_is_nothrow_swap =
172 boost::is_nothrow_swappable<hash_nothrow_swap>::value;
173
174// Check that the traits work when expected.
175 BOOST_TEST(have_is_nothrow_move);
176 BOOST_TEST(have_is_nothrow_move_assign);
177 BOOST_TEST(have_is_nothrow_swap);
178
179 BOOST_LIGHTWEIGHT_TEST_OSTREAM
180 << "have_is_nothrow_move: " << have_is_nothrow_move << std::endl
181 << "have_is_nothrow_swap: " << have_is_nothrow_swap << std::endl;
182 }
183
184 UNORDERED_AUTO_TEST (test_noexcept) {
185 if (have_is_nothrow_move) {
186#ifdef BOOST_UNORDERED_FOA_TESTS
187 BOOST_TEST((boost::is_nothrow_move_constructible<
188 boost::unordered_flat_set<int> >::value));
189 BOOST_TEST((boost::is_nothrow_move_constructible<
190 boost::unordered_flat_map<int, int> >::value));
191 BOOST_TEST((boost::is_nothrow_move_constructible<
192 boost::unordered_node_set<int> >::value));
193 BOOST_TEST((boost::is_nothrow_move_constructible<
194 boost::unordered_node_map<int, int> >::value));
195#else
196 BOOST_TEST((boost::is_nothrow_move_constructible<
197 boost::unordered_set<int> >::value));
198 BOOST_TEST((boost::is_nothrow_move_constructible<
199 boost::unordered_multiset<int> >::value));
200 BOOST_TEST((boost::is_nothrow_move_constructible<
201 boost::unordered_map<int, int> >::value));
202 BOOST_TEST((boost::is_nothrow_move_constructible<
203 boost::unordered_multimap<int, int> >::value));
204#endif
205 }
206
207#ifdef BOOST_UNORDERED_FOA_TESTS
208 BOOST_TEST(
209 (!boost::is_nothrow_move_constructible<
210 boost::unordered_flat_set<int, hash_possible_exception> >::value));
211 BOOST_TEST(
212 (!boost::is_nothrow_move_constructible<boost::unordered_flat_set<int,
213 boost::hash<int>, equal_to_possible_exception> >::value));
214
215 BOOST_TEST(
216 (!boost::is_nothrow_move_constructible<
217 boost::unordered_node_set<int, hash_possible_exception> >::value));
218 BOOST_TEST(
219 (!boost::is_nothrow_move_constructible<boost::unordered_node_set<int,
220 boost::hash<int>, equal_to_possible_exception> >::value));
221#else
222 BOOST_TEST((!boost::is_nothrow_move_constructible<
223 boost::unordered_set<int, hash_possible_exception> >::value));
224 BOOST_TEST(
225 (!boost::is_nothrow_move_constructible<boost::unordered_multiset<int,
226 boost::hash<int>, equal_to_possible_exception> >::value));
227#endif
228 }
229
230 template <class X> static void test_nothrow_move_when_noexcept(X*)
231 {
232 if (have_is_nothrow_move) {
233 BOOST_TEST(boost::is_nothrow_move_constructible<X>::value);
234 }
235
236 throwing_test_exception = false;
237
238 X x1;
239 x1.insert(10);
240 x1.insert(50);
241
242 try {
243 throwing_test_exception = true;
244
245 X x2 = std::move(x1);
246 BOOST_TEST(x2.size() == 2);
247 BOOST_TEST(*x2.begin() == 10 || *x2.begin() == 50);
248 BOOST_TEST(have_is_nothrow_move);
249 } catch (test_exception) {
250 BOOST_TEST(!have_is_nothrow_move);
251 }
252
253 throwing_test_exception = false;
254 }
255
256 template <class T>
257 void test_nothrow_move_assign_when_noexcept(T*, test::random_generator const&)
258 {
259 {
260 if (have_is_nothrow_move_assign) {
261 BOOST_TEST(boost::is_nothrow_move_assignable<T>::value);
262 }
263
264 throwing_test_exception = false;
265
266 T x1;
267 T x2;
268 x1.insert(10);
269 x1.insert(50);
270 for (int i = 0; i < 100; ++i) {
271 x2.insert(i);
272 }
273
274 try {
275 throwing_test_exception = true;
276
277 x2 = std::move(x1);
278 BOOST_TEST(x2.size() == 2);
279 BOOST_TEST(*x2.begin() == 10 || *x2.begin() == 50);
280 BOOST_TEST(have_is_nothrow_move_assign);
281 } catch (test_exception) {
282 BOOST_TEST(!have_is_nothrow_move_assign);
283 }
284
285 throwing_test_exception = false;
286 }
287
288 {
289 if (have_is_nothrow_move_assign) {
290 BOOST_TEST(boost::is_nothrow_move_assignable<T>::value);
291 }
292
293 throwing_test_exception = false;
294
295 T x1;
296 T x2;
297 x1.insert(10);
298 x1.insert(50);
299 for (int i = 0; i < 100; ++i) {
300 x2.insert(i);
301 }
302
303 try {
304 throwing_test_exception = true;
305
306 x1 = std::move(x2);
307 BOOST_TEST(x1.size() == 100);
308 BOOST_TEST(have_is_nothrow_move_assign);
309 } catch (test_exception) {
310 BOOST_TEST(!have_is_nothrow_move_assign);
311 }
312
313 throwing_test_exception = false;
314 }
315 }
316
317 template <class X> static void test_nothrow_swap_when_noexcept(X*)
318 {
319 if (have_is_nothrow_swap) {
320 BOOST_TEST(boost::is_nothrow_swappable<X>::value);
321 }
322
323 throwing_test_exception = false;
324
325 X x1;
326 X x2;
327 x1.insert(10);
328 x1.insert(50);
329 for (int i = 0; i < 100; ++i) {
330 x2.insert(i);
331 }
332
333 try {
334 throwing_test_exception = true;
335
336 x1.swap(x2);
337 BOOST_TEST(x1.size() == 100);
338 BOOST_TEST(x2.size() == 2);
339 BOOST_TEST(*x2.begin() == 10 || *x2.begin() == 50);
340 BOOST_TEST(have_is_nothrow_swap);
341 } catch (test_exception) {
342 BOOST_TEST(!have_is_nothrow_swap);
343 }
344
345 throwing_test_exception = false;
346 }
347} // namespace noexcept_tests
348
349#if defined(BOOST_MSVC)
350#pragma warning(pop)
351#endif
352
353template <class T> class allocator1
354{
355 allocator1 operator=(allocator1 const&);
356 allocator1 operator=(allocator1&&);
357
358public:
359 typedef T value_type;
360
361 allocator1() {}
362 allocator1(allocator1 const&) {}
363
364 template <class U> allocator1(allocator1<U> const&) {}
365
366 T* allocate(std::size_t n)
367 {
368 noexcept_tests::test_throw(name: "Allocate");
369 return static_cast<T*>(::operator new(n * sizeof(T)));
370 }
371
372 void deallocate(T* p, std::size_t) { ::operator delete(p); }
373
374 friend bool operator==(allocator1 const&, allocator1 const&) { return true; }
375 friend bool operator!=(allocator1 const&, allocator1 const&) { return false; }
376};
377
378template <class T> class allocator2
379{
380 allocator2 operator=(allocator2 const&);
381
382public:
383 typedef T value_type;
384 typedef boost::true_type propagate_on_container_move_assignment;
385
386 allocator2() {}
387 allocator2(allocator2 const&) {}
388
389 template <class U> allocator2(allocator2<U> const&) {}
390
391 allocator2& operator=(allocator2&&) { return *this; }
392
393 T* allocate(std::size_t n)
394 {
395 noexcept_tests::test_throw(name: "Allocate");
396 return static_cast<T*>(::operator new(n * sizeof(T)));
397 }
398
399 void deallocate(T* p, std::size_t) { ::operator delete(p); }
400
401 friend bool operator==(allocator2 const&, allocator2 const&) { return true; }
402 friend bool operator!=(allocator2 const&, allocator2 const&) { return false; }
403};
404
405UNORDERED_AUTO_TEST (prelim_allocator_checks) {
406 BOOST_TEST(boost::allocator_is_always_equal<allocator1<int> >::type::value);
407 BOOST_TEST(!boost::allocator_propagate_on_container_move_assignment<
408 allocator1<int> >::type::value);
409
410 BOOST_TEST(boost::allocator_is_always_equal<allocator2<int> >::type::value);
411 BOOST_TEST(boost::allocator_propagate_on_container_move_assignment<
412 allocator2<int> >::type::value);
413}
414
415using test::default_generator;
416
417#ifdef BOOST_UNORDERED_FOA_TESTS
418boost::unordered_flat_set<int, noexcept_tests::hash_nothrow_move_construct,
419 noexcept_tests::equal_to_nothrow_move_construct>* throwing_set;
420boost::unordered_flat_set<int, noexcept_tests::hash_nothrow_swap,
421 noexcept_tests::equal_to_nothrow_swap>* throwing_set2;
422
423boost::unordered_flat_set<int, noexcept_tests::hash_nothrow_swap,
424 noexcept_tests::equal_to_nothrow_swap, allocator1<int> >* throwing_set_alloc1;
425
426boost::unordered_flat_set<int, noexcept_tests::hash_nothrow_swap,
427 noexcept_tests::equal_to_nothrow_swap, allocator2<int> >* throwing_set_alloc2;
428
429boost::unordered_node_set<int, noexcept_tests::hash_nothrow_move_construct,
430 noexcept_tests::equal_to_nothrow_move_construct>* throwing_node_set;
431boost::unordered_node_set<int, noexcept_tests::hash_nothrow_swap,
432 noexcept_tests::equal_to_nothrow_swap>* throwing_node_set2;
433
434boost::unordered_node_set<int, noexcept_tests::hash_nothrow_swap,
435 noexcept_tests::equal_to_nothrow_swap, allocator1<int> >*
436 throwing_node_set_alloc1;
437
438boost::unordered_node_set<int, noexcept_tests::hash_nothrow_swap,
439 noexcept_tests::equal_to_nothrow_swap, allocator2<int> >*
440 throwing_node_set_alloc2;
441
442// clang-format off
443UNORDERED_TEST(
444 test_nothrow_move_when_noexcept, ((throwing_set)(throwing_node_set)))
445UNORDERED_TEST(
446 test_nothrow_swap_when_noexcept, ((throwing_set2)(throwing_node_set2)))
447UNORDERED_TEST(test_nothrow_move_assign_when_noexcept,
448 ((throwing_set_alloc1)(throwing_set_alloc2)
449 (throwing_node_set_alloc1)(throwing_node_set_alloc2))(
450 (default_generator)))
451// clang-format on
452#else
453boost::unordered_set<int, noexcept_tests::hash_nothrow_move_construct,
454 noexcept_tests::equal_to_nothrow_move_construct>* throwing_set;
455
456boost::unordered_set<int, noexcept_tests::hash_nothrow_swap,
457 noexcept_tests::equal_to_nothrow_swap>* throwing_set2;
458
459boost::unordered_set<int, noexcept_tests::hash_nothrow_move_assign,
460 noexcept_tests::equal_to_nothrow_move_assign, allocator1<int> >*
461 throwing_set_alloc1;
462
463boost::unordered_set<int, noexcept_tests::hash_nothrow_move_assign,
464 noexcept_tests::equal_to_nothrow_move_assign, allocator2<int> >*
465 throwing_set_alloc2;
466
467UNORDERED_TEST(test_nothrow_move_when_noexcept, ((throwing_set)))
468UNORDERED_TEST(test_nothrow_swap_when_noexcept, ((throwing_set2)))
469UNORDERED_TEST(test_nothrow_move_assign_when_noexcept,
470 ((throwing_set_alloc1)(throwing_set_alloc2))((default_generator)))
471#endif
472
473RUN_TESTS()
474

source code of boost/libs/unordered/test/unordered/noexcept_tests.cpp