]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/unordered/test/unordered/unnecessary_copy_tests.cpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / libs / unordered / test / unordered / unnecessary_copy_tests.cpp
CommitLineData
7c673cae
FG
1
2// Copyright 2006-2009 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
b32b8144 6// clang-format off
7c673cae
FG
7#include "../helpers/prefix.hpp"
8#include <boost/unordered_set.hpp>
9#include <boost/unordered_map.hpp>
10#include "../helpers/postfix.hpp"
b32b8144 11// clang-format on
7c673cae 12
7c673cae
FG
13#include "../helpers/test.hpp"
14
b32b8144
FG
15namespace unnecessary_copy_tests {
16 struct count_copies
17 {
18 private:
19 BOOST_COPYABLE_AND_MOVABLE(count_copies)
20 public:
21 static int copies;
22 static int moves;
23 static int id_count;
24
25 count_copies() : tag_(0), id_(++id_count)
7c673cae 26 {
b32b8144
FG
27 ++copies;
28 trace_op("Default construct");
7c673cae
FG
29 }
30
b32b8144
FG
31 explicit count_copies(int tag) : tag_(tag), id_(++id_count)
32 {
33 ++copies;
34 trace_op("Tag construct");
7c673cae
FG
35 }
36
b32b8144
FG
37 // This bizarre constructor is an attempt to confuse emplace.
38 //
39 // unordered_map<count_copies, count_copies> x:
40 // x.emplace(count_copies(1), count_copies(2));
41 // x.emplace(count_copies(1), count_copies(2), count_copies(3));
42 //
43 // The first emplace should use the single argument constructor twice.
44 // The second emplace should use the single argument contructor for
45 // the key, and this constructor for the value.
46 count_copies(count_copies const&, count_copies const& x)
47 : tag_(x.tag_), id_(++id_count)
48 {
49 ++copies;
50 trace_op("Pair construct");
51 }
7c673cae 52
b32b8144
FG
53 count_copies(count_copies const& x) : tag_(x.tag_), id_(++id_count)
54 {
55 ++copies;
56 trace_op("Copy construct");
7c673cae 57 }
b32b8144
FG
58
59 count_copies(BOOST_RV_REF(count_copies) x) : tag_(x.tag_), id_(++id_count)
60 {
61 x.tag_ = -1;
62 ++moves;
63 trace_op("Move construct");
64 }
65
66 count_copies& operator=(
67 BOOST_COPY_ASSIGN_REF(count_copies) p) // Copy assignment
68 {
69 tag_ = p.tag_;
70 ++copies;
71 trace_op("Copy assign");
72 return *this;
73 }
74
75 count_copies& operator=(BOOST_RV_REF(count_copies) p) // Move assignment
76 {
77 tag_ = p.tag_;
78 ++moves;
79 trace_op("Move assign");
80 return *this;
81 }
82
83 ~count_copies() { trace_op("Destruct"); }
84
85 void trace_op(char const* str)
86 {
87 BOOST_LIGHTWEIGHT_TEST_OSTREAM << str << ": " << tag_ << " (#" << id_
88 << ")" << std::endl;
89 }
90
91 int tag_;
92 int id_;
93 };
94
95 bool operator==(count_copies const& x, count_copies const& y)
96 {
97 return x.tag_ == y.tag_;
98 }
99
100 template <class T> T source() { return T(); }
101
102 void reset()
103 {
104 count_copies::copies = 0;
105 count_copies::moves = 0;
106
107 BOOST_LIGHTWEIGHT_TEST_OSTREAM << "\nReset\n" << std::endl;
108 }
7c673cae
FG
109}
110
111#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
112namespace boost
113#else
114namespace unnecessary_copy_tests
115#endif
116{
b32b8144
FG
117 std::size_t hash_value(unnecessary_copy_tests::count_copies const& x)
118 {
119 return static_cast<std::size_t>(x.tag_);
120 }
7c673cae
FG
121}
122
123// Boost.Move doesn't seem to work very well on this compiler.
124// For example for:
125//
126// T x;
127//
128// It will default construct T, and then move it in.
129// For 'T const' it seems to copy.
130
131#if defined(__IBMCPP__) && __IBMCPP__ <= 1210
132#define EXTRA_CONSTRUCT_COST 1
133#else
134#define EXTRA_CONSTRUCT_COST 0
135#endif
136
b32b8144
FG
137#define COPY_COUNT(n) \
138 if (::unnecessary_copy_tests::count_copies::copies != n) { \
139 BOOST_ERROR("Wrong number of copies."); \
140 BOOST_LIGHTWEIGHT_TEST_OSTREAM \
141 << "Number of copies: " \
142 << ::unnecessary_copy_tests::count_copies::copies << " expecting: " << n \
143 << std::endl; \
144 }
145#define MOVE_COUNT(n) \
146 if (::unnecessary_copy_tests::count_copies::moves != n) { \
147 BOOST_ERROR("Wrong number of moves."); \
148 BOOST_LIGHTWEIGHT_TEST_OSTREAM \
149 << "Number of moves: " << ::unnecessary_copy_tests::count_copies::moves \
150 << " expecting: " << n << std::endl; \
151 }
152#define COPY_COUNT_RANGE(a, b) \
153 if (::unnecessary_copy_tests::count_copies::copies < a || \
154 ::unnecessary_copy_tests::count_copies::copies > b) { \
155 BOOST_ERROR("Wrong number of copies."); \
156 BOOST_LIGHTWEIGHT_TEST_OSTREAM \
157 << "Number of copies: " \
158 << ::unnecessary_copy_tests::count_copies::copies << " expecting: [" \
159 << a << ", " << b << "]" << std::endl; \
160 }
161#define MOVE_COUNT_RANGE(a, b) \
162 if (::unnecessary_copy_tests::count_copies::moves < a || \
163 ::unnecessary_copy_tests::count_copies::moves > b) { \
164 BOOST_ERROR("Wrong number of moves."); \
165 BOOST_LIGHTWEIGHT_TEST_OSTREAM \
166 << "Number of moves: " << ::unnecessary_copy_tests::count_copies::moves \
167 << " expecting: [" << a << ", " << b << "]" << std::endl; \
168 }
169#define COPY_COUNT_EXTRA(a, b) COPY_COUNT_RANGE(a, a + b * EXTRA_CONSTRUCT_COST)
170#define MOVE_COUNT_EXTRA(a, b) MOVE_COUNT_RANGE(a, a + b * EXTRA_CONSTRUCT_COST)
171
172namespace unnecessary_copy_tests {
173 int count_copies::copies;
174 int count_copies::moves;
175 int count_copies::id_count;
176
177 template <class T> void unnecessary_copy_insert_test(T*)
178 {
179 T x;
11fdf7f2 180 typename T::value_type a;
b32b8144
FG
181 reset();
182 x.insert(a);
183 COPY_COUNT(1);
184 MOVE_COUNT(0);
185 }
186
187 template <class T> void unnecessary_copy_insert_rvalue_set_test(T*)
188 {
189 T x;
11fdf7f2 190 typename T::value_type a;
b32b8144
FG
191 reset();
192 x.insert(boost::move(a));
193 COPY_COUNT(0);
194 MOVE_COUNT(1);
195
11fdf7f2 196 typename T::value_type a2;
b32b8144
FG
197 reset();
198 x.insert(boost::move(a));
199 COPY_COUNT(0);
200 MOVE_COUNT((x.size() == 2 ? 1 : 0));
201 }
202
203 template <class T> void unnecessary_copy_insert_rvalue_map_test(T*)
204 {
205 // Doesn't currently try to emulate std::pair move construction,
206 // so std::pair's require a copy. Could try emulating it in
207 // construct_from_args.
208
209 T x;
11fdf7f2 210 typename T::value_type a;
b32b8144
FG
211 reset();
212 x.insert(boost::move(a));
213#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
214 COPY_COUNT(1);
215 MOVE_COUNT(0);
216#else
217 COPY_COUNT(0);
218 MOVE_COUNT(1);
219#endif
7c673cae 220
11fdf7f2 221 typename T::value_type a2;
b32b8144
FG
222 reset();
223 x.insert(boost::move(a));
224#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
225 COPY_COUNT((x.size() == 2 ? 1 : 0));
226 MOVE_COUNT(0);
227#else
228 COPY_COUNT(0);
229 MOVE_COUNT((x.size() == 2 ? 1 : 0));
230#endif
231 }
232
233 boost::unordered_set<count_copies>* set;
234 boost::unordered_multiset<count_copies>* multiset;
235 boost::unordered_map<int, count_copies>* map;
236 boost::unordered_multimap<int, count_copies>* multimap;
237
238 UNORDERED_TEST(unnecessary_copy_insert_test, ((set)(multiset)(map)(multimap)))
239 UNORDERED_TEST(unnecessary_copy_insert_rvalue_set_test, ((set)(multiset)))
240 UNORDERED_TEST(unnecessary_copy_insert_rvalue_map_test, ((map)(multimap)))
241
242 template <class T> void unnecessary_copy_emplace_test(T*)
243 {
244 reset();
245 T x;
11fdf7f2 246 typename T::value_type a;
b32b8144
FG
247 COPY_COUNT(1);
248 x.emplace(a);
249 COPY_COUNT(2);
250 }
251
252 template <class T> void unnecessary_copy_emplace_rvalue_test(T*)
253 {
254 reset();
255 T x;
11fdf7f2 256 x.emplace(source<typename T::value_type>());
7c673cae 257#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
b32b8144 258 COPY_COUNT(1);
7c673cae 259#else
b32b8144 260 COPY_COUNT(2);
7c673cae 261#endif
b32b8144 262 }
7c673cae 263
b32b8144
FG
264 UNORDERED_TEST(
265 unnecessary_copy_emplace_test, ((set)(multiset)(map)(multimap)))
266 UNORDERED_TEST(
267 unnecessary_copy_emplace_rvalue_test, ((set)(multiset)(map)(multimap)))
7c673cae
FG
268
269#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
b32b8144
FG
270 template <class T> void unnecessary_copy_emplace_std_move_test(T*)
271 {
272 reset();
273 T x;
11fdf7f2 274 typename T::value_type a;
b32b8144
FG
275 COPY_COUNT(1);
276 MOVE_COUNT(0);
277 x.emplace(std::move(a));
278 COPY_COUNT(1);
279 MOVE_COUNT(1);
280 }
281
282 UNORDERED_TEST(
283 unnecessary_copy_emplace_std_move_test, ((set)(multiset)(map)(multimap)))
7c673cae
FG
284#endif
285
b32b8144
FG
286 template <class T> void unnecessary_copy_emplace_boost_move_test(T*)
287 {
288 reset();
289 T x;
11fdf7f2 290 typename T::value_type a;
b32b8144
FG
291 COPY_COUNT(1);
292 MOVE_COUNT_EXTRA(0, 1);
293 x.emplace(boost::move(a));
7c673cae 294#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
b32b8144
FG
295 COPY_COUNT(1);
296 MOVE_COUNT(1);
7c673cae 297#else
b32b8144
FG
298 // Since std::pair isn't movable, move only works for sets.
299 COPY_COUNT_RANGE(1, 2);
300 MOVE_COUNT_RANGE(0, 1);
7c673cae 301#endif
b32b8144
FG
302 }
303
304 UNORDERED_TEST(
305 unnecessary_copy_emplace_boost_move_test, ((set)(multiset)(map)(multimap)))
306
307 template <class T> void unnecessary_copy_emplace_boost_move_set_test(T*)
308 {
309 reset();
310 T x;
11fdf7f2 311 typename T::value_type a;
b32b8144
FG
312 COPY_COUNT(1);
313 MOVE_COUNT(0);
314 x.emplace(boost::move(a));
315 COPY_COUNT(1);
316 MOVE_COUNT(1);
317 }
318
319 UNORDERED_TEST(
320 unnecessary_copy_emplace_boost_move_set_test, ((set)(multiset)))
321
322 template <class T> void unnecessary_copy_emplace_boost_move_map_test(T*)
323 {
324 reset();
325 T x;
326 COPY_COUNT(0);
327 MOVE_COUNT(0);
11fdf7f2 328 typename T::value_type a;
b32b8144
FG
329 COPY_COUNT(1);
330 MOVE_COUNT_EXTRA(0, 1);
331 x.emplace(boost::move(a));
7c673cae 332#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
b32b8144
FG
333 COPY_COUNT(2);
334 MOVE_COUNT_EXTRA(0, 1);
7c673cae 335#else
b32b8144
FG
336 COPY_COUNT(1);
337 MOVE_COUNT(1);
7c673cae 338#endif
b32b8144 339 }
7c673cae 340
b32b8144
FG
341 UNORDERED_TEST(
342 unnecessary_copy_emplace_boost_move_map_test, ((map)(multimap)))
7c673cae 343
b32b8144
FG
344 UNORDERED_AUTO_TEST (unnecessary_copy_emplace_set_test) {
345 // When calling 'source' the object is moved on some compilers, but not
346 // others. So count that here to adjust later.
7c673cae 347
b32b8144
FG
348 reset();
349 source<count_copies>();
350 int source_cost = ::unnecessary_copy_tests::count_copies::moves;
7c673cae 351
b32b8144 352 //
7c673cae 353
b32b8144
FG
354 reset();
355 boost::unordered_set<count_copies> x;
356 count_copies a;
357 x.insert(a);
358 COPY_COUNT(2);
359 MOVE_COUNT(0);
7c673cae 360
b32b8144
FG
361//
362// 0 arguments
363//
7c673cae 364
b32b8144
FG
365#if !BOOST_UNORDERED_SUN_WORKAROUNDS1
366 // The container will have to create a copy in order to compare with
367 // the existing element.
368 reset();
369 x.emplace();
7c673cae 370
b32b8144
FG
371 // source_cost doesn't make much sense here, but it seems to fit.
372 COPY_COUNT(1);
373 MOVE_COUNT(source_cost);
7c673cae
FG
374#endif
375
b32b8144
FG
376 //
377 // 1 argument
378 //
379
380 // Emplace should be able to tell that there already is an element
381 // without creating a new one.
382 reset();
383 x.emplace(a);
384 COPY_COUNT(0);
385 MOVE_COUNT(0);
386
387 // A new object is created by source, but it shouldn't be moved or
388 // copied.
389 reset();
390 x.emplace(source<count_copies>());
391 COPY_COUNT(1);
392 MOVE_COUNT(source_cost);
393
394 // No move should take place.
395 reset();
396 x.emplace(boost::move(a));
397 COPY_COUNT(0);
398 MOVE_COUNT(0);
399
400 // Use a new value for cases where a did get moved...
401 count_copies b;
402
403 // The container will have to create a copy in order to compare with
404 // the existing element.
405 reset();
406 x.emplace(b.tag_);
407 COPY_COUNT(1);
408 MOVE_COUNT(0);
409
410 //
411 // 2 arguments
412 //
413
414 // The container will have to create b copy in order to compare with
415 // the existing element.
416 //
417 // Note to self: If copy_count == 0 it's an error not an optimization.
418 // TODO: Devise a better test.
419
420 reset();
421
422 x.emplace(b, b);
423 COPY_COUNT(1);
424 MOVE_COUNT(0);
425 }
426
427 UNORDERED_AUTO_TEST (unnecessary_copy_emplace_map_test) {
428 // When calling 'source' the object is moved on some compilers, but not
429 // others. So count that here to adjust later.
430
431 reset();
432 source<count_copies>();
433 int source_cost = ::unnecessary_copy_tests::count_copies::moves;
434
435 reset();
436 source<std::pair<count_copies, count_copies> >();
437 int source_pair_cost = ::unnecessary_copy_tests::count_copies::moves;
438
439 //
440
441 reset();
442 boost::unordered_map<count_copies, count_copies> x;
443 // TODO: Run tests for pairs without const etc.
444 std::pair<count_copies const, count_copies> a;
445 x.emplace(a);
446 COPY_COUNT_EXTRA(4, 1);
447 MOVE_COUNT_EXTRA(0, 1);
7c673cae 448
b32b8144
FG
449//
450// 0 arguments
451//
7c673cae 452
b32b8144
FG
453#if !BOOST_UNORDERED_SUN_WORKAROUNDS1
454 // COPY_COUNT(1) would be okay here.
455 reset();
456 x.emplace();
457#if BOOST_WORKAROUND(BOOST_MSVC, == 1700)
458 // This is a little odd, Visual C++ 11 seems to move the pair, which
459 // results in one copy (for the const key) and one move (for the
460 // non-const mapped value). Since 'emplace(boost::move(a))' (see below)
461 // has the normal result, it must be some odd consequence of how
462 // Visual C++ 11 handles calling move for default arguments.
463 COPY_COUNT(3);
464 MOVE_COUNT(1);
465#else
466 COPY_COUNT_EXTRA(2, 1);
467 MOVE_COUNT_EXTRA(0, 1);
468#endif
469#endif
7c673cae 470
b32b8144
FG
471 reset();
472 x.emplace(boost::unordered::piecewise_construct, boost::make_tuple(),
473 boost::make_tuple());
474 COPY_COUNT(2);
475 MOVE_COUNT(0);
476
477 //
478 // 1 argument
479 //
480
481 reset();
482 x.emplace(a);
483 COPY_COUNT(0);
484 MOVE_COUNT(0);
485
486 // A new object is created by source, but it shouldn't be moved or
487 // copied.
488 reset();
489 x.emplace(source<std::pair<count_copies, count_copies> >());
490 COPY_COUNT(2);
491 MOVE_COUNT(source_pair_cost);
492
493#if !(defined(__GNUC__) && __cplusplus < 199900L) && \
494 !(defined(_MSC_VER) && _MSC_VER < 1600)
495 count_copies part;
496 reset();
497 std::pair<count_copies const&, count_copies const&> a_ref(part, part);
498 x.emplace(a_ref);
499 COPY_COUNT(2);
500 MOVE_COUNT(0);
7c673cae
FG
501
502#endif
503
b32b8144
FG
504 // No move should take place.
505 // (since a is already in the container)
506 reset();
507 x.emplace(boost::move(a));
508 COPY_COUNT(0);
509 MOVE_COUNT(0);
510
511 //
512 // 2 arguments
513 //
514
515 std::pair<count_copies const, count_copies> b;
516
517 reset();
518 x.emplace(b.first, b.second);
519 COPY_COUNT(0);
520 MOVE_COUNT(0);
521
522 reset();
523 x.emplace(source<count_copies>(), source<count_copies>());
524 COPY_COUNT(2);
525 MOVE_COUNT(source_cost * 2);
526
527 // source<count_copies> creates a single copy.
528 reset();
529 x.emplace(b.first, source<count_copies>());
530 COPY_COUNT(1);
531 MOVE_COUNT(source_cost);
532
533 reset();
534 x.emplace(count_copies(b.first.tag_), count_copies(b.second.tag_));
535 COPY_COUNT(2);
536 MOVE_COUNT(0);
537
538 reset();
539 x.emplace(boost::unordered::piecewise_construct,
540 boost::make_tuple(boost::ref(b.first)),
541 boost::make_tuple(boost::ref(b.second)));
542 COPY_COUNT(0);
543 MOVE_COUNT(0);
544
545#if BOOST_UNORDERED_TUPLE_ARGS
546
547 reset();
548 x.emplace(boost::unordered::piecewise_construct,
549 std::make_tuple(std::ref(b.first)), std::make_tuple(std::ref(b.second)));
550 COPY_COUNT(0);
551 MOVE_COUNT(0);
552
553 std::pair<count_copies const, count_copies> move_source_trial;
554 reset();
1e59de90
TL
555 (void)std::make_tuple(std::move(move_source_trial.first));
556 (void)std::make_tuple(std::move(move_source_trial.second));
b32b8144
FG
557 int tuple_move_cost = ::unnecessary_copy_tests::count_copies::moves;
558 int tuple_copy_cost = ::unnecessary_copy_tests::count_copies::copies;
559
560 std::pair<count_copies const, count_copies> move_source;
561 reset();
562 x.emplace(boost::unordered::piecewise_construct,
563 std::make_tuple(std::move(move_source.first)),
564 std::make_tuple(std::move(move_source.second)));
565 COPY_COUNT(tuple_copy_cost);
566 MOVE_COUNT(tuple_move_cost);
567
568#if !defined(BOOST_NO_CXX11_HDR_TUPLE) && \
569 !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6) && \
570 !(defined(BOOST_MSVC) && BOOST_MSVC < 1700)
571 reset();
572 x.emplace(boost::unordered::piecewise_construct,
573 std::forward_as_tuple(b.first), std::forward_as_tuple(b.second));
574 COPY_COUNT(0);
575 MOVE_COUNT(0);
7c673cae
FG
576#endif
577
578#endif
b32b8144 579 }
7c673cae
FG
580}
581
582RUN_TESTS()