1// Copyright (C) 2023 Christian Mazakas
2// Copyright (C) 2023 Joaquin M Lopez Munoz
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#include "helpers.hpp"
7
8#include <boost/config.hpp>
9#include <boost/unordered/concurrent_flat_map.hpp>
10#include <boost/unordered/concurrent_flat_set.hpp>
11
12#include <boost/core/ignore_unused.hpp>
13
14#if defined(BOOST_MSVC)
15#pragma warning(disable : 4127) // conditional expression is constant
16#endif
17
18struct raii_convertible
19{
20 int x = 0, y = 0 ;
21
22 template <typename T>
23 raii_convertible(T const & t) : x{t.x_} {}
24
25 template <typename T, typename Q>
26 raii_convertible(std::pair<T, Q> const & p) : x{p.first.x_}, y{p.second.x_}
27 {}
28
29 operator raii() { return {x}; }
30 operator std::pair<raii const, raii>() { return {x, y}; }
31};
32
33namespace {
34 test::seed_t initialize_seed(78937);
35
36 struct lvalue_inserter_type
37 {
38 template <class T, class X> void operator()(std::vector<T>& values, X& x)
39 {
40 static constexpr auto value_type_cardinality =
41 value_cardinality<typename X::value_type>::value;
42
43 std::atomic<std::uint64_t> num_inserts{0};
44 thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
45 for (auto const& r : s) {
46 bool b = x.insert(r);
47 if (b) {
48 ++num_inserts;
49 }
50 }
51 });
52 BOOST_TEST_EQ(num_inserts, x.size());
53 BOOST_TEST_EQ(
54 raii::copy_constructor, value_type_cardinality * x.size());
55 BOOST_TEST_EQ(raii::copy_assignment, 0u);
56 BOOST_TEST_EQ(raii::move_assignment, 0u);
57 }
58 } lvalue_inserter;
59
60 struct norehash_lvalue_inserter_type : public lvalue_inserter_type
61 {
62 template <class T, class X> void operator()(std::vector<T>& values, X& x)
63 {
64 static constexpr auto value_type_cardinality =
65 value_cardinality<typename X::value_type>::value;
66
67 x.reserve(values.size());
68 lvalue_inserter_type::operator()(values, x);
69 BOOST_TEST_EQ(
70 raii::copy_constructor, value_type_cardinality * x.size());
71 BOOST_TEST_EQ(raii::move_constructor, 0u);
72 }
73 } norehash_lvalue_inserter;
74
75 struct rvalue_inserter_type
76 {
77 template <class T, class X> void operator()(std::vector<T>& values, X& x)
78 {
79 BOOST_TEST_EQ(raii::copy_constructor, 0u);
80
81 std::atomic<std::uint64_t> num_inserts{0};
82 thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
83 for (auto& r : s) {
84 bool b = x.insert(std::move(r));
85 if (b) {
86 ++num_inserts;
87 }
88 }
89 });
90 BOOST_TEST_EQ(num_inserts, x.size());
91
92 if (std::is_same<T, typename X::value_type>::value &&
93 !std::is_same<typename X::key_type, typename X::value_type>::value) {
94 BOOST_TEST_EQ(raii::copy_constructor, x.size());
95 } else {
96 BOOST_TEST_EQ(raii::copy_constructor, 0u);
97 }
98
99 BOOST_TEST_EQ(raii::copy_assignment, 0u);
100 BOOST_TEST_EQ(raii::move_assignment, 0u);
101 }
102 } rvalue_inserter;
103
104 struct norehash_rvalue_inserter_type : public rvalue_inserter_type
105 {
106 template <class T, class X> void operator()(std::vector<T>& values, X& x)
107 {
108 static constexpr auto value_type_cardinality =
109 value_cardinality<typename X::value_type>::value;
110
111 x.reserve(values.size());
112
113 BOOST_TEST_EQ(raii::copy_constructor, 0u);
114 BOOST_TEST_EQ(raii::move_constructor, 0u);
115
116 rvalue_inserter_type::operator()(values, x);
117
118 if (std::is_same<T, typename X::value_type>::value) {
119 if (std::is_same<typename X::key_type,
120 typename X::value_type>::value) {
121 BOOST_TEST_EQ(raii::copy_constructor, 0u);
122 BOOST_TEST_EQ(raii::move_constructor, x.size());
123 }
124 else {
125 BOOST_TEST_EQ(raii::copy_constructor, x.size());
126 BOOST_TEST_EQ(raii::move_constructor, x.size());
127 }
128 } else {
129 BOOST_TEST_EQ(raii::copy_constructor, 0u);
130 BOOST_TEST_EQ(
131 raii::move_constructor, value_type_cardinality * x.size());
132 }
133 }
134 } norehash_rvalue_inserter;
135
136 struct iterator_range_inserter_type
137 {
138 template <class T, class X> void operator()(std::vector<T>& values, X& x)
139 {
140 static constexpr auto value_type_cardinality =
141 value_cardinality<typename X::value_type>::value;
142
143 std::vector<raii_convertible> values2;
144 values2.reserve(n: values.size());
145 for (auto const& v : values) {
146 values2.push_back(x: raii_convertible(v));
147 }
148
149 thread_runner(values2, [&x](boost::span<raii_convertible> s) {
150 x.insert(s.begin(), s.end());
151 });
152
153 BOOST_TEST_EQ(
154 raii::default_constructor, value_type_cardinality * values2.size());
155#if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
156 BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
157 // some versions of old gcc have trouble eliding copies here
158 // https://godbolt.org/z/Ebo6TbvaG
159#elif BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
160 BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000)
161 // seemingly same problem, though the snippet above does not reveal it
162#else
163 BOOST_TEST_EQ(raii::copy_constructor, 0u);
164#endif
165 BOOST_TEST_EQ(raii::copy_assignment, 0u);
166 BOOST_TEST_EQ(raii::move_assignment, 0u);
167 }
168 } iterator_range_inserter;
169
170 struct lvalue_insert_or_assign_copy_assign_type
171 {
172 template <class T, class X> void operator()(std::vector<T>& values, X& x)
173 {
174 thread_runner(values, [&x](boost::span<T> s) {
175 for (auto& r : s) {
176 x.insert_or_assign(r.first, r.second);
177 }
178 });
179
180 BOOST_TEST_EQ(raii::default_constructor, 0u);
181 BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
182 // don't check move construction count here because of rehashing
183 BOOST_TEST_GT(raii::move_constructor, 0u);
184 BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
185 BOOST_TEST_EQ(raii::move_assignment, 0u);
186 }
187 } lvalue_insert_or_assign_copy_assign;
188
189 struct lvalue_insert_or_assign_move_assign_type
190 {
191 template <class T, class X> void operator()(std::vector<T>& values, X& x)
192 {
193 thread_runner(values, [&x](boost::span<T> s) {
194 for (auto& r : s) {
195 x.insert_or_assign(r.first, std::move(r.second));
196 }
197 });
198
199 BOOST_TEST_EQ(raii::default_constructor, 0u);
200 BOOST_TEST_EQ(raii::copy_constructor, x.size());
201 BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
202 BOOST_TEST_EQ(raii::copy_assignment, 0u);
203 BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
204 }
205 } lvalue_insert_or_assign_move_assign;
206
207 struct rvalue_insert_or_assign_copy_assign_type
208 {
209 template <class T, class X> void operator()(std::vector<T>& values, X& x)
210 {
211 thread_runner(values, [&x](boost::span<T> s) {
212 for (auto& r : s) {
213 x.insert_or_assign(std::move(r.first), r.second);
214 }
215 });
216
217 BOOST_TEST_EQ(raii::default_constructor, 0u);
218 BOOST_TEST_EQ(raii::copy_constructor, x.size());
219 BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
220 BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
221 BOOST_TEST_EQ(raii::move_assignment, 0u);
222 }
223 } rvalue_insert_or_assign_copy_assign;
224
225 struct rvalue_insert_or_assign_move_assign_type
226 {
227 template <class T, class X> void operator()(std::vector<T>& values, X& x)
228 {
229 thread_runner(values, [&x](boost::span<T> s) {
230 for (auto& r : s) {
231 x.insert_or_assign(std::move(r.first), std::move(r.second));
232 }
233 });
234
235 BOOST_TEST_EQ(raii::default_constructor, 0u);
236 BOOST_TEST_EQ(raii::copy_constructor, 0u);
237 BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
238 BOOST_TEST_EQ(raii::copy_assignment, 0u);
239 BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
240 }
241 } rvalue_insert_or_assign_move_assign;
242
243 struct trans_insert_or_assign_copy_assign_type
244 {
245 template <class T, class X> void operator()(std::vector<T>& values, X& x)
246 {
247 using is_transparent =
248 typename boost::make_void<typename X::hasher::is_transparent,
249 typename X::key_equal::is_transparent>::type;
250
251 boost::ignore_unused<is_transparent>();
252
253 BOOST_TEST_EQ(raii::default_constructor, 0u);
254
255 thread_runner(values, [&x](boost::span<T> s) {
256 for (auto& r : s) {
257 x.insert_or_assign(r.first.x_, r.second);
258 }
259 });
260
261 BOOST_TEST_EQ(raii::default_constructor, x.size());
262 BOOST_TEST_EQ(raii::copy_constructor, x.size());
263 BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
264 BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
265 BOOST_TEST_EQ(raii::move_assignment, 0u);
266 }
267 } trans_insert_or_assign_copy_assign;
268
269 struct trans_insert_or_assign_move_assign_type
270 {
271 template <class T, class X> void operator()(std::vector<T>& values, X& x)
272 {
273 using is_transparent =
274 typename boost::make_void<typename X::hasher::is_transparent,
275 typename X::key_equal::is_transparent>::type;
276
277 boost::ignore_unused<is_transparent>();
278
279 thread_runner(values, [&x](boost::span<T> s) {
280 for (auto& r : s) {
281 x.insert_or_assign(r.first.x_, std::move(r.second));
282 }
283 });
284
285 BOOST_TEST_EQ(raii::default_constructor, x.size());
286 BOOST_TEST_EQ(raii::copy_constructor, 0u);
287 BOOST_TEST_GT(raii::move_constructor, 2 * x.size()); // rehashing
288 BOOST_TEST_EQ(raii::copy_assignment, 0u);
289 BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
290 }
291 } trans_insert_or_assign_move_assign;
292
293 struct lvalue_insert_or_cvisit_type
294 {
295 template <class T, class X> void operator()(std::vector<T>& values, X& x)
296 {
297 static constexpr auto value_type_cardinality =
298 value_cardinality<typename X::value_type>::value;
299
300 std::atomic<std::uint64_t> num_inserts{0};
301 std::atomic<std::uint64_t> num_invokes{0};
302 thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
303 for (auto& r : s) {
304 bool b = x.insert_or_cvisit(
305 r, [&num_invokes](typename X::value_type const& v) {
306 (void)v;
307 ++num_invokes;
308 });
309
310 if (b) {
311 ++num_inserts;
312 }
313 }
314 });
315
316 BOOST_TEST_EQ(num_inserts, x.size());
317 BOOST_TEST_EQ(num_invokes, values.size() - x.size());
318
319 BOOST_TEST_EQ(raii::default_constructor, 0u);
320 BOOST_TEST_EQ(
321 raii::copy_constructor, value_type_cardinality * x.size());
322 // don't check move construction count here because of rehashing
323 BOOST_TEST_GT(raii::move_constructor, 0u);
324 BOOST_TEST_EQ(raii::move_assignment, 0u);
325 }
326 } lvalue_insert_or_cvisit;
327
328 struct lvalue_insert_or_visit_type
329 {
330 template <class T, class X> void operator()(std::vector<T>& values, X& x)
331 {
332 static constexpr auto value_type_cardinality =
333 value_cardinality<typename X::value_type>::value;
334
335 // concurrent_flat_set visit is always const access
336 using arg_type = typename std::conditional<
337 std::is_same<typename X::key_type, typename X::value_type>::value,
338 typename X::value_type const,
339 typename X::value_type
340 >::type;
341
342 std::atomic<std::uint64_t> num_inserts{0};
343 std::atomic<std::uint64_t> num_invokes{0};
344 thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
345 for (auto& r : s) {
346 bool b =
347 x.insert_or_visit(r, [&num_invokes](arg_type& v) {
348 (void)v;
349 ++num_invokes;
350 });
351
352 if (b) {
353 ++num_inserts;
354 }
355 }
356 });
357
358 BOOST_TEST_EQ(num_inserts, x.size());
359 BOOST_TEST_EQ(num_invokes, values.size() - x.size());
360
361 BOOST_TEST_EQ(raii::default_constructor, 0u);
362 BOOST_TEST_EQ(raii::copy_constructor, value_type_cardinality * x.size());
363 // don't check move construction count here because of rehashing
364 BOOST_TEST_GT(raii::move_constructor, 0u);
365 BOOST_TEST_EQ(raii::move_assignment, 0u);
366 }
367 } lvalue_insert_or_visit;
368
369 struct rvalue_insert_or_cvisit_type
370 {
371 template <class T, class X> void operator()(std::vector<T>& values, X& x)
372 {
373 static constexpr auto value_type_cardinality =
374 value_cardinality<typename X::value_type>::value;
375
376 std::atomic<std::uint64_t> num_inserts{0};
377 std::atomic<std::uint64_t> num_invokes{0};
378 thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
379 for (auto& r : s) {
380 bool b = x.insert_or_cvisit(
381 std::move(r), [&num_invokes](typename X::value_type const& v) {
382 (void)v;
383 ++num_invokes;
384 });
385
386 if (b) {
387 ++num_inserts;
388 }
389 }
390 });
391
392 BOOST_TEST_EQ(num_inserts, x.size());
393 BOOST_TEST_EQ(num_invokes, values.size() - x.size());
394
395 BOOST_TEST_EQ(raii::default_constructor, 0u);
396
397 if (std::is_same<T, typename X::value_type>::value) {
398 if (std::is_same<typename X::key_type,
399 typename X::value_type>::value) {
400 BOOST_TEST_EQ(raii::copy_constructor, 0u);
401 BOOST_TEST_GE(raii::move_constructor, x.size());
402 }
403 else {
404 BOOST_TEST_EQ(raii::copy_constructor, x.size());
405 BOOST_TEST_GE(raii::move_constructor, x.size());
406 }
407 } else {
408 BOOST_TEST_EQ(raii::copy_constructor, 0u);
409 BOOST_TEST_GE(
410 raii::move_constructor, value_type_cardinality * x.size());
411 }
412 }
413 } rvalue_insert_or_cvisit;
414
415 struct rvalue_insert_or_visit_type
416 {
417 template <class T, class X> void operator()(std::vector<T>& values, X& x)
418 {
419 static constexpr auto value_type_cardinality =
420 value_cardinality<typename X::value_type>::value;
421
422 // concurrent_flat_set visit is always const access
423 using arg_type = typename std::conditional<
424 std::is_same<typename X::key_type, typename X::value_type>::value,
425 typename X::value_type const,
426 typename X::value_type
427 >::type;
428
429 std::atomic<std::uint64_t> num_inserts{0};
430 std::atomic<std::uint64_t> num_invokes{0};
431 thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
432 for (auto& r : s) {
433 bool b = x.insert_or_visit(
434 std::move(r), [&num_invokes](arg_type& v) {
435 (void)v;
436 ++num_invokes;
437 });
438
439 if (b) {
440 ++num_inserts;
441 }
442 }
443 });
444
445 BOOST_TEST_EQ(num_inserts, x.size());
446 BOOST_TEST_EQ(num_invokes, values.size() - x.size());
447
448 BOOST_TEST_EQ(raii::default_constructor, 0u);
449 if (std::is_same<T, typename X::value_type>::value) {
450 if (std::is_same<typename X::key_type,
451 typename X::value_type>::value) {
452 BOOST_TEST_EQ(raii::copy_constructor, 0u);
453 BOOST_TEST_GE(raii::move_constructor, x.size());
454 }
455 else {
456 BOOST_TEST_EQ(raii::copy_constructor, x.size());
457 BOOST_TEST_GE(raii::move_constructor, x.size());
458 }
459 } else {
460 BOOST_TEST_EQ(raii::copy_constructor, 0u);
461 BOOST_TEST_GE(
462 raii::move_constructor, value_type_cardinality * x.size());
463 }
464 }
465 } rvalue_insert_or_visit;
466
467 struct iterator_range_insert_or_cvisit_type
468 {
469 template <class T, class X> void operator()(std::vector<T>& values, X& x)
470 {
471 static constexpr auto value_type_cardinality =
472 value_cardinality<typename X::value_type>::value;
473
474 std::vector<raii_convertible> values2;
475 values2.reserve(n: values.size());
476 for (auto const& v : values) {
477 values2.push_back(x: raii_convertible(v));
478 }
479
480 std::atomic<std::uint64_t> num_invokes{0};
481 thread_runner(
482 values2, [&x, &num_invokes](boost::span<raii_convertible> s) {
483 x.insert_or_cvisit(s.begin(), s.end(),
484 [&num_invokes](typename X::value_type const& v) {
485 (void)v;
486 ++num_invokes;
487 });
488 });
489
490 BOOST_TEST_EQ(num_invokes, values.size() - x.size());
491
492 BOOST_TEST_EQ(
493 raii::default_constructor, value_type_cardinality * values2.size());
494#if (BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
495 BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)) || \
496 (BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
497 BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000))
498 // skip test
499#else
500 BOOST_TEST_EQ(raii::copy_constructor, 0u);
501#endif
502 BOOST_TEST_GT(raii::move_constructor, 0u);
503 }
504 } iterator_range_insert_or_cvisit;
505
506 struct iterator_range_insert_or_visit_type
507 {
508 template <class T, class X> void operator()(std::vector<T>& values, X& x)
509 {
510 static constexpr auto value_type_cardinality =
511 value_cardinality<typename X::value_type>::value;
512
513 std::vector<raii_convertible> values2;
514 values2.reserve(n: values.size());
515 for (auto const& v : values) {
516 values2.push_back(x: raii_convertible(v));
517 }
518
519 std::atomic<std::uint64_t> num_invokes{0};
520 thread_runner(
521 values2, [&x, &num_invokes](boost::span<raii_convertible> s) {
522 x.insert_or_visit(s.begin(), s.end(),
523 [&num_invokes](typename X::value_type const& v) {
524 (void)v;
525 ++num_invokes;
526 });
527 });
528
529 BOOST_TEST_EQ(num_invokes, values.size() - x.size());
530
531 BOOST_TEST_EQ(
532 raii::default_constructor, value_type_cardinality * values2.size());
533#if (BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
534 BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)) || \
535 (BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
536 BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000))
537 // skip test
538#else
539 BOOST_TEST_EQ(raii::copy_constructor, 0u);
540#endif
541 BOOST_TEST_GT(raii::move_constructor, 0u);
542 }
543 } iterator_range_insert_or_visit;
544
545 template <class X, class GF, class F>
546 void insert(X*, GF gen_factory, F inserter, test::random_generator rg)
547 {
548 auto gen = gen_factory.template get<X>();
549 auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
550 auto reference_cont = reference_container<X>(values.begin(), values.end());
551 raii::reset_counts();
552
553 {
554 X x;
555
556 inserter(values, x);
557
558 BOOST_TEST_EQ(x.size(), reference_cont.size());
559
560 using value_type = typename X::value_type;
561 BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) {
562 BOOST_TEST(reference_cont.contains(get_key(v)));
563 if (rg == test::sequential) {
564 BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
565 }
566 }));
567 }
568
569 BOOST_TEST_GE(raii::default_constructor, 0u);
570 BOOST_TEST_GE(raii::copy_constructor, 0u);
571 BOOST_TEST_GE(raii::move_constructor, 0u);
572 BOOST_TEST_GT(raii::destructor, 0u);
573
574 BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
575 raii::move_constructor,
576 raii::destructor);
577 }
578
579 template <class X, class IL>
580 void insert_initializer_list(std::pair<X*, IL> p)
581 {
582 using value_type = typename X::value_type;
583 // concurrent_flat_set visit is always const access
584 using arg_type = typename std::conditional<
585 std::is_same<typename X::key_type, typename X::value_type>::value,
586 typename X::value_type const,
587 typename X::value_type
588 >::type;
589
590 auto init_list = p.second;
591 std::vector<raii> dummy;
592 auto reference_cont = reference_container<X>(
593 init_list.begin(), init_list.end());
594 raii::reset_counts();
595
596 {
597 {
598 X x;
599
600 thread_runner(
601 dummy, [&x, &init_list](boost::span<raii>) { x.insert(init_list); });
602
603 BOOST_TEST_EQ(x.size(), reference_cont.size());
604
605 BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) {
606 BOOST_TEST(reference_cont.contains(get_key(v)));
607 BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
608 }));
609 }
610
611 BOOST_TEST_GE(raii::default_constructor, 0u);
612 BOOST_TEST_GE(raii::copy_constructor, 0u);
613 BOOST_TEST_GE(raii::move_constructor, 0u);
614 BOOST_TEST_GT(raii::destructor, 0u);
615
616 BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
617 raii::move_constructor,
618 raii::destructor);
619
620 BOOST_TEST_EQ(raii::copy_assignment, 0u);
621 BOOST_TEST_EQ(raii::move_assignment, 0u);
622 }
623
624 {
625 {
626 std::atomic<std::uint64_t> num_invokes{0};
627
628 X x;
629
630 thread_runner(dummy, [&x, &init_list, &num_invokes](boost::span<raii>) {
631 x.insert_or_visit(init_list, [&num_invokes](arg_type& v) {
632 (void)v;
633 ++num_invokes;
634 });
635
636 x.insert_or_cvisit(
637 init_list, [&num_invokes](typename X::value_type const& v) {
638 (void)v;
639 ++num_invokes;
640 });
641 });
642
643 BOOST_TEST_EQ(num_invokes, (init_list.size() - x.size()) +
644 (num_threads - 1) * init_list.size() +
645 num_threads * init_list.size());
646 BOOST_TEST_EQ(x.size(), reference_cont.size());
647
648 BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) {
649 BOOST_TEST(reference_cont.contains(get_key(v)));
650 BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
651 }));
652 }
653
654 BOOST_TEST_GE(raii::default_constructor, 0u);
655 BOOST_TEST_GE(raii::copy_constructor, 0u);
656 BOOST_TEST_GE(raii::move_constructor, 0u);
657 BOOST_TEST_GT(raii::destructor, 0u);
658
659 BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
660 raii::move_constructor,
661 raii::destructor);
662
663 BOOST_TEST_EQ(raii::copy_assignment, 0u);
664 BOOST_TEST_EQ(raii::move_assignment, 0u);
665 }
666 }
667
668 UNORDERED_AUTO_TEST (insert_sfinae_test) {
669 // mostly a compile-time tests to ensure that there's no ambiguity when a
670 // user does this
671 using value_type =
672 typename boost::unordered::concurrent_flat_map<raii, raii>::value_type;
673 boost::unordered::concurrent_flat_map<raii, raii> x;
674 x.insert(obj: {1, 2});
675
676 x.insert_or_visit(obj: {2, 3}, f: [](value_type&) {});
677 x.insert_or_cvisit(obj: {3, 4}, f: [](value_type const&) {});
678 }
679
680 boost::unordered::concurrent_flat_map<raii, raii>* map;
681 boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
682 transp_key_equal>* trans_map;
683 boost::unordered::concurrent_flat_map<raii, raii, boost::hash<raii>,
684 std::equal_to<raii>, fancy_allocator<std::pair<raii const, raii> > >*
685 fancy_map;
686
687 boost::unordered::concurrent_flat_set<raii>* set;
688 boost::unordered::concurrent_flat_set<raii, boost::hash<raii>,
689 std::equal_to<raii>, fancy_allocator<std::pair<raii const, raii> > >*
690 fancy_set;
691
692 std::initializer_list<std::pair<raii const, raii> > map_init_list{
693 {raii{0}, raii{0}},
694 {raii{1}, raii{1}},
695 {raii{2}, raii{2}},
696 {raii{3}, raii{3}},
697 {raii{4}, raii{4}},
698 {raii{5}, raii{5}},
699 {raii{6}, raii{6}},
700 {raii{6}, raii{6}},
701 {raii{7}, raii{7}},
702 {raii{8}, raii{8}},
703 {raii{9}, raii{9}},
704 {raii{10}, raii{10}},
705 {raii{9}, raii{9}},
706 {raii{8}, raii{8}},
707 {raii{7}, raii{7}},
708 {raii{6}, raii{6}},
709 {raii{5}, raii{5}},
710 {raii{4}, raii{4}},
711 {raii{3}, raii{3}},
712 {raii{2}, raii{2}},
713 {raii{1}, raii{1}},
714 {raii{0}, raii{0}},
715 };
716
717 std::initializer_list<raii> set_init_list{
718 raii{0},
719 raii{1},
720 raii{2},
721 raii{3},
722 raii{4},
723 raii{5},
724 raii{6},
725 raii{6},
726 raii{7},
727 raii{8},
728 raii{9},
729 raii{10},
730 raii{9},
731 raii{8},
732 raii{7},
733 raii{6},
734 raii{5},
735 raii{4},
736 raii{3},
737 raii{2},
738 raii{1},
739 raii{0},
740 };
741
742 auto map_and_init_list=std::make_pair(x&: map,y&: map_init_list);
743 auto set_and_init_list=std::make_pair(x&: set,y&: set_init_list);
744
745} // namespace
746
747using test::default_generator;
748using test::limited_range;
749using test::sequential;
750
751// clang-format off
752UNORDERED_TEST(
753 insert_initializer_list,
754 ((map_and_init_list)(set_and_init_list)))
755
756UNORDERED_TEST(
757 insert,
758 ((map)(fancy_map)(set)(fancy_set))
759 ((value_type_generator_factory)(init_type_generator_factory))
760 ((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter)
761 (norehash_lvalue_inserter)(norehash_rvalue_inserter)
762 (lvalue_insert_or_cvisit)(lvalue_insert_or_visit)
763 (rvalue_insert_or_cvisit)(rvalue_insert_or_visit)
764 (iterator_range_insert_or_cvisit)(iterator_range_insert_or_visit))
765 ((default_generator)(sequential)(limited_range)))
766
767UNORDERED_TEST(
768 insert,
769 ((map))
770 ((init_type_generator_factory))
771 ((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)
772 (rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign))
773 ((default_generator)(sequential)(limited_range)))
774
775UNORDERED_TEST(
776 insert,
777 ((trans_map))
778 ((init_type_generator_factory))
779 ((trans_insert_or_assign_copy_assign)(trans_insert_or_assign_move_assign))
780 ((default_generator)(sequential)(limited_range)))
781// clang-format on
782
783RUN_TESTS()
784

source code of boost/libs/unordered/test/cfoa/insert_tests.cpp