1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // (C) Copyright Ion Gaztanaga 2004-2013. Distributed under the Boost
4 // Software License, Version 1.0. (See accompanying file
5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // See http://www.boost.org/libs/container for documentation.
8 //
9 //////////////////////////////////////////////////////////////////////////////
10 #include <set>
11 #include <boost/container/set.hpp>
12 #include <boost/container/adaptive_pool.hpp>
13
14 #include "print_container.hpp"
15 #include "movable_int.hpp"
16 #include "dummy_test_allocator.hpp"
17 #include "set_test.hpp"
18 #include "propagate_allocator_test.hpp"
19 #include "emplace_test.hpp"
20 #include "../../intrusive/test/iterator_test.hpp"
21
22 using namespace boost::container;
23
24 //Test recursive structures
25 class recursive_set
26 {
27 public:
operator =(const recursive_set & x)28 recursive_set & operator=(const recursive_set &x)
29 { id_ = x.id_; set_ = x.set_; return *this; }
30
31 int id_;
32 set<recursive_set> set_;
33 set<recursive_set>::iterator it_;
34 set<recursive_set>::const_iterator cit_;
35 set<recursive_set>::reverse_iterator rit_;
36 set<recursive_set>::const_reverse_iterator crit_;
37
operator <(const recursive_set & a,const recursive_set & b)38 friend bool operator< (const recursive_set &a, const recursive_set &b)
39 { return a.id_ < b.id_; }
40 };
41
42 //Test recursive structures
43 class recursive_multiset
44 {
45 public:
operator =(const recursive_multiset & x)46 recursive_multiset & operator=(const recursive_multiset &x)
47 { id_ = x.id_; multiset_ = x.multiset_; return *this; }
48
49 int id_;
50 multiset<recursive_multiset> multiset_;
51 multiset<recursive_multiset>::iterator it_;
52 multiset<recursive_multiset>::const_iterator cit_;
53 multiset<recursive_multiset>::reverse_iterator rit_;
54 multiset<recursive_multiset>::const_reverse_iterator crit_;
55
operator <(const recursive_multiset & a,const recursive_multiset & b)56 friend bool operator< (const recursive_multiset &a, const recursive_multiset &b)
57 { return a.id_ < b.id_; }
58 };
59
60 template<class C>
test_move()61 void test_move()
62 {
63 //Now test move semantics
64 C original;
65 original.emplace();
66 C move_ctor(boost::move(original));
67 C move_assign;
68 move_assign.emplace();
69 move_assign = boost::move(move_ctor);
70 move_assign.swap(original);
71 }
72
node_type_test()73 bool node_type_test()
74 {
75 using namespace boost::container;
76 {
77 typedef set<test::movable_int> set_type;
78 set_type src;
79 {
80 test::movable_int mv_1(1), mv_2(2), mv_3(3);
81 src.emplace(boost::move(mv_1));
82 src.emplace(boost::move(mv_2));
83 src.emplace(boost::move(mv_3));
84 }
85 if(src.size() != 3)
86 return false;
87
88 set_type dst;
89 {
90 test::movable_int mv_3(3);
91 dst.emplace(boost::move(mv_3));
92 }
93
94 if(dst.size() != 1)
95 return false;
96
97 const test::movable_int mv_1(1);
98 const test::movable_int mv_2(2);
99 const test::movable_int mv_3(3);
100 const test::movable_int mv_33(33);
101 set_type::insert_return_type r;
102
103 r = dst.insert(src.extract(mv_33)); // Key version, try to insert empty node
104 if(! (r.position == dst.end() && r.inserted == false && r.node.empty()) )
105 return false;
106 r = dst.insert(src.extract(src.find(mv_1))); // Iterator version, successful
107 if(! (r.position == dst.find(mv_1) && r.inserted == true && r.node.empty()) )
108 return false;
109 r = dst.insert(dst.begin(), src.extract(mv_2)); // Key type version, successful
110 if(! (r.position == dst.find(mv_2) && r.inserted == true && r.node.empty()) )
111 return false;
112 r = dst.insert(src.extract(mv_3)); // Key type version, unsuccessful
113
114 if(!src.empty())
115 return false;
116 if(dst.size() != 3)
117 return false;
118 if(! (r.position == dst.find(mv_3) && r.inserted == false && r.node.value() == mv_3) )
119 return false;
120 }
121
122 {
123 typedef multiset<test::movable_int> multiset_type;
124 multiset_type src;
125 {
126 test::movable_int mv_1(1), mv_2(2), mv_3(3), mv_3bis(3);
127 src.emplace(boost::move(mv_1));
128 src.emplace(boost::move(mv_2));
129 src.emplace(boost::move(mv_3));
130 src.emplace_hint(src.begin(), boost::move(mv_3bis));
131 }
132 if(src.size() != 4)
133 return false;
134
135 multiset_type dst;
136 {
137 test::movable_int mv_3(3);
138 dst.emplace(boost::move(mv_3));
139 }
140
141 if(dst.size() != 1)
142 return false;
143
144 const test::movable_int mv_1(1);
145 const test::movable_int mv_2(2);
146 const test::movable_int mv_3(3);
147 const test::movable_int mv_4(4);
148 multiset_type::iterator r;
149
150 multiset_type::node_type nt(src.extract(mv_3));
151 r = dst.insert(dst.begin(), boost::move(nt));
152 if(! (*r == mv_3 && dst.find(mv_3) == r && nt.empty()) )
153 return false;
154
155 nt = src.extract(src.find(mv_1));
156 r = dst.insert(boost::move(nt)); // Iterator version, successful
157 if(! (*r == mv_1 && nt.empty()) )
158 return false;
159
160 nt = src.extract(mv_2);
161 r = dst.insert(boost::move(nt)); // Key type version, successful
162 if(! (*r == mv_2 && nt.empty()) )
163 return false;
164
165 r = dst.insert(src.extract(mv_3)); // Key type version, successful
166 if(! (*r == mv_3 && r == --multiset_type::iterator(dst.upper_bound(mv_3)) && nt.empty()) )
167 return false;
168
169 r = dst.insert(src.extract(mv_4)); // Key type version, unsuccessful
170 if(! (r == dst.end()) )
171 return false;
172
173 if(!src.empty())
174 return false;
175 if(dst.size() != 5)
176 return false;
177 }
178 return true;
179 }
180
181 struct boost_container_set;
182 struct boost_container_multiset;
183
184 namespace boost {
185 namespace container {
186 namespace test {
187
188 template<>
189 struct alloc_propagate_base<boost_container_set>
190 {
191 template <class T, class Allocator>
192 struct apply
193 {
194 typedef boost::container::set<T, std::less<T>, Allocator> type;
195 };
196 };
197
198 template<>
199 struct alloc_propagate_base<boost_container_multiset>
200 {
201 template <class T, class Allocator>
202 struct apply
203 {
204 typedef boost::container::multiset<T, std::less<T>, Allocator> type;
205 };
206 };
207
constructor_template_auto_deduction_test()208 bool constructor_template_auto_deduction_test()
209 {
210 #ifndef BOOST_CONTAINER_NO_CXX17_CTAD
211 using namespace boost::container;
212 const std::size_t NumElements = 100;
213 {
214 std::set<int> int_set;
215 for (std::size_t i = 0; i != NumElements; ++i) {
216 int_set.insert(static_cast<int>(i));
217 }
218 std::multiset<int> int_mset;
219 for (std::size_t i = 0; i != NumElements; ++i) {
220 int_mset.insert(static_cast<int>(i));
221 }
222
223 typedef std::less<int> comp_int_t;
224 typedef std::allocator<int> alloc_int_t;
225
226 //range
227 {
228 auto fset = set(int_set.begin(), int_set.end());
229 if (!CheckEqualContainers(int_set, fset))
230 return false;
231 auto fmset = multiset(int_mset.begin(), int_mset.end());
232 if (!CheckEqualContainers(int_mset, fmset))
233 return false;
234 }
235 //range+comp
236 {
237 auto fset = set(int_set.begin(), int_set.end(), comp_int_t());
238 if (!CheckEqualContainers(int_set, fset))
239 return false;
240 auto fmset = multiset(int_mset.begin(), int_mset.end(), comp_int_t());
241 if (!CheckEqualContainers(int_mset, fmset))
242 return false;
243 }
244 //range+comp+alloc
245 {
246 auto fset = set(int_set.begin(), int_set.end(), comp_int_t(), alloc_int_t());
247 if (!CheckEqualContainers(int_set, fset))
248 return false;
249 auto fmset = multiset(int_mset.begin(), int_mset.end(), comp_int_t(), alloc_int_t());
250 if (!CheckEqualContainers(int_mset, fmset))
251 return false;
252 }
253 //range+alloc
254 {
255 auto fset = set(int_set.begin(), int_set.end(), alloc_int_t());
256 if (!CheckEqualContainers(int_set, fset))
257 return false;
258 auto fmset = multiset(int_mset.begin(), int_mset.end(), alloc_int_t());
259 if (!CheckEqualContainers(int_mset, fmset))
260 return false;
261 }
262
263 //ordered_unique_range / ordered_range
264
265 //range
266 {
267 auto fset = set(ordered_unique_range, int_set.begin(), int_set.end());
268 if (!CheckEqualContainers(int_set, fset))
269 return false;
270 auto fmset = multiset(ordered_range, int_mset.begin(), int_mset.end());
271 if (!CheckEqualContainers(int_mset, fmset))
272 return false;
273 }
274 //range+comp
275 {
276 auto fset = set(ordered_unique_range, int_set.begin(), int_set.end(), comp_int_t());
277 if (!CheckEqualContainers(int_set, fset))
278 return false;
279 auto fmset = multiset(ordered_range, int_mset.begin(), int_mset.end(), comp_int_t());
280 if (!CheckEqualContainers(int_mset, fmset))
281 return false;
282 }
283 //range+comp+alloc
284 {
285 auto fset = set(ordered_unique_range, int_set.begin(), int_set.end(), comp_int_t(), alloc_int_t());
286 if (!CheckEqualContainers(int_set, fset))
287 return false;
288 auto fmset = multiset(ordered_range, int_mset.begin(), int_mset.end(), comp_int_t(), alloc_int_t());
289 if (!CheckEqualContainers(int_mset, fmset))
290 return false;
291 }
292 //range+alloc
293 {
294 auto fset = set(ordered_unique_range, int_set.begin(), int_set.end(), alloc_int_t());
295 if (!CheckEqualContainers(int_set, fset))
296 return false;
297 auto fmset = multiset(ordered_range, int_mset.begin(), int_mset.end(), alloc_int_t());
298 if (!CheckEqualContainers(int_mset, fmset))
299 return false;
300 }
301 }
302 #endif
303 return true;
304 }
305
306 }}} //boost::container::test
307
308 template<class VoidAllocator, boost::container::tree_type_enum tree_type_value>
309 struct GetAllocatorSet
310 {
311 template<class ValueType>
312 struct apply
313 {
314 typedef set < ValueType
315 , std::less<ValueType>
316 , typename allocator_traits<VoidAllocator>
317 ::template portable_rebind_alloc<ValueType>::type
318 , typename boost::container::tree_assoc_options
319 < boost::container::tree_type<tree_type_value>
320 >::type
321 > set_type;
322
323 typedef multiset < ValueType
324 , std::less<ValueType>
325 , typename allocator_traits<VoidAllocator>
326 ::template portable_rebind_alloc<ValueType>::type
327 , typename boost::container::tree_assoc_options
328 < boost::container::tree_type<tree_type_value>
329 >::type
330 > multiset_type;
331 };
332 };
333
test_merge_from_different_comparison()334 void test_merge_from_different_comparison()
335 {
336 set<int> set1;
337 set<int, std::greater<int> > set2;
338 set1.merge(set2);
339 }
340
test_heterogeneous_lookups()341 bool test_heterogeneous_lookups()
342 {
343 typedef set<int, test::less_transparent> set_t;
344 typedef multiset<int, test::less_transparent> mset_t;
345
346 set_t set1;
347 mset_t mset1;
348
349 const set_t &cset1 = set1;
350 const mset_t &cmset1 = mset1;
351
352 set1.insert(1);
353 set1.insert(1);
354 set1.insert(2);
355 set1.insert(2);
356 set1.insert(3);
357
358 mset1.insert(1);
359 mset1.insert(1);
360 mset1.insert(2);
361 mset1.insert(2);
362 mset1.insert(3);
363
364 const test::non_copymovable_int find_me(2);
365
366 //find
367 if(*set1.find(find_me) != 2)
368 return false;
369 if(*cset1.find(find_me) != 2)
370 return false;
371 if(*mset1.find(find_me) != 2)
372 return false;
373 if(*cmset1.find(find_me) != 2)
374 return false;
375
376 //count
377 if(set1.count(find_me) != 1)
378 return false;
379 if(cset1.count(find_me) != 1)
380 return false;
381 if(mset1.count(find_me) != 2)
382 return false;
383 if(cmset1.count(find_me) != 2)
384 return false;
385
386 //contains
387 if(!set1.contains(find_me))
388 return false;
389 if(!cset1.contains(find_me))
390 return false;
391 if(!mset1.contains(find_me))
392 return false;
393 if(!cmset1.contains(find_me))
394 return false;
395
396 //lower_bound
397 if(*set1.lower_bound(find_me) != 2)
398 return false;
399 if(*cset1.lower_bound(find_me) != 2)
400 return false;
401 if(*mset1.lower_bound(find_me) != 2)
402 return false;
403 if(*cmset1.lower_bound(find_me) != 2)
404 return false;
405
406 //upper_bound
407 if(*set1.upper_bound(find_me) != 3)
408 return false;
409 if(*cset1.upper_bound(find_me) != 3)
410 return false;
411 if(*mset1.upper_bound(find_me) != 3)
412 return false;
413 if(*cmset1.upper_bound(find_me) != 3)
414 return false;
415
416 //equal_range
417 if(*set1.equal_range(find_me).first != 2)
418 return false;
419 if(*cset1.equal_range(find_me).second != 3)
420 return false;
421 if(*mset1.equal_range(find_me).first != 2)
422 return false;
423 if(*cmset1.equal_range(find_me).second != 3)
424 return false;
425
426 return true;
427 }
428
main()429 int main ()
430 {
431 //Recursive container instantiation
432 {
433 set<recursive_set> set_;
434 multiset<recursive_multiset> multiset_;
435 }
436 //Allocator argument container
437 {
438 set<int> set_((set<int>::allocator_type()));
439 multiset<int> multiset_((multiset<int>::allocator_type()));
440 }
441 //Now test move semantics
442 {
443 test_move<set<recursive_set> >();
444 test_move<multiset<recursive_multiset> >();
445 }
446 //Test std::pair value type as tree has workarounds to make old std::pair
447 //implementations movable that can break things
448 {
449 boost::container::set<std::pair<int,int> > s;
450 std::pair<int,int> p(0, 0);
451 s.insert(p);
452 s.emplace(p);
453 }
454
455 if (!boost::container::test::instantiate_constructors<set<int>, multiset<int> >())
456 return 1;
457
458 test_merge_from_different_comparison();
459
460 ////////////////////////////////////
461 // Constructor Template Auto Deduction test
462 ////////////////////////////////////
463 if (!test::constructor_template_auto_deduction_test()) {
464 return 1;
465 }
466
467 if(!test_heterogeneous_lookups())
468 return 1;
469
470 ////////////////////////////////////
471 // Testing allocator implementations
472 ////////////////////////////////////
473 {
474 typedef std::set<int> MyStdSet;
475 typedef std::multiset<int> MyStdMultiSet;
476
477 if (0 != test::set_test
478 < GetAllocatorSet<std::allocator<void>, red_black_tree>::apply<int>::set_type
479 , MyStdSet
480 , GetAllocatorSet<std::allocator<void>, red_black_tree>::apply<int>::multiset_type
481 , MyStdMultiSet>()) {
482 std::cout << "Error in set_test<std::allocator<void>, red_black_tree>" << std::endl;
483 return 1;
484 }
485
486 if (0 != test::set_test
487 < GetAllocatorSet<new_allocator<void>, avl_tree>::apply<int>::set_type
488 , MyStdSet
489 , GetAllocatorSet<new_allocator<void>, avl_tree>::apply<int>::multiset_type
490 , MyStdMultiSet>()) {
491 std::cout << "Error in set_test<new_allocator<void>, avl_tree>" << std::endl;
492 return 1;
493 }
494
495 if (0 != test::set_test
496 < GetAllocatorSet<adaptive_pool<void>, scapegoat_tree>::apply<int>::set_type
497 , MyStdSet
498 , GetAllocatorSet<adaptive_pool<void>, scapegoat_tree>::apply<int>::multiset_type
499 , MyStdMultiSet>()) {
500 std::cout << "Error in set_test<adaptive_pool<void>, scapegoat_tree>" << std::endl;
501 return 1;
502 }
503
504 ///////////
505
506 if (0 != test::set_test
507 < GetAllocatorSet<new_allocator<void>, splay_tree>::apply<test::movable_int>::set_type
508 , MyStdSet
509 , GetAllocatorSet<new_allocator<void>, splay_tree>::apply<test::movable_int>::multiset_type
510 , MyStdMultiSet>()) {
511 std::cout << "Error in set_test<new_allocator<void>, splay_tree>" << std::endl;
512 return 1;
513 }
514
515 if (0 != test::set_test
516 < GetAllocatorSet<new_allocator<void>, red_black_tree>::apply<test::copyable_int>::set_type
517 , MyStdSet
518 , GetAllocatorSet<new_allocator<void>, red_black_tree>::apply<test::copyable_int>::multiset_type
519 , MyStdMultiSet>()) {
520 std::cout << "Error in set_test<new_allocator<void>, red_black_tree>" << std::endl;
521 return 1;
522 }
523
524 if (0 != test::set_test
525 < GetAllocatorSet<new_allocator<void>, red_black_tree>::apply<test::movable_and_copyable_int>::set_type
526 , MyStdSet
527 , GetAllocatorSet<new_allocator<void>, red_black_tree>::apply<test::movable_and_copyable_int>::multiset_type
528 , MyStdMultiSet>()) {
529 std::cout << "Error in set_test<new_allocator<void>, red_black_tree>" << std::endl;
530 return 1;
531 }
532 }
533
534 ////////////////////////////////////
535 // Emplace testing
536 ////////////////////////////////////
537 const test::EmplaceOptions SetOptions = (test::EmplaceOptions)(test::EMPLACE_HINT | test::EMPLACE_ASSOC);
538 if(!boost::container::test::test_emplace<set<test::EmplaceInt>, SetOptions>())
539 return 1;
540 if(!boost::container::test::test_emplace<multiset<test::EmplaceInt>, SetOptions>())
541 return 1;
542
543 ////////////////////////////////////
544 // Allocator propagation testing
545 ////////////////////////////////////
546 if(!boost::container::test::test_propagate_allocator<boost_container_set>())
547 return 1;
548
549 if(!boost::container::test::test_propagate_allocator<boost_container_multiset>())
550 return 1;
551
552 if (!boost::container::test::test_set_methods_with_initializer_list_as_argument_for<set<int> >())
553 return 1;
554
555 if (!boost::container::test::test_set_methods_with_initializer_list_as_argument_for<multiset<int> >())
556 return 1;
557
558 ////////////////////////////////////
559 // Test optimize_size option
560 ////////////////////////////////////
561 //
562 // set
563 //
564 typedef set< int*, std::less<int*>, std::allocator<int*>
565 , tree_assoc_options< optimize_size<false>, tree_type<red_black_tree> >::type > rbset_size_optimized_no;
566
567 typedef set< int*, std::less<int*>, std::allocator<int*>
568 , tree_assoc_options< optimize_size<true>, tree_type<avl_tree> >::type > avlset_size_optimized_yes;
569 //
570 // multiset
571 //
572 typedef multiset< int*, std::less<int*>, std::allocator<int*>
573 , tree_assoc_options< optimize_size<true>, tree_type<red_black_tree> >::type > rbmset_size_optimized_yes;
574
575 typedef multiset< int*, std::less<int*>, std::allocator<int*>
576 , tree_assoc_options< optimize_size<false>, tree_type<avl_tree> >::type > avlmset_size_optimized_no;
577
578 BOOST_STATIC_ASSERT(sizeof(rbmset_size_optimized_yes) < sizeof(rbset_size_optimized_no));
579 BOOST_STATIC_ASSERT(sizeof(avlset_size_optimized_yes) < sizeof(avlmset_size_optimized_no));
580
581 ////////////////////////////////////
582 // Iterator testing
583 ////////////////////////////////////
584 {
585 typedef boost::container::set<int> cont_int;
586 cont_int a; a.insert(0); a.insert(1); a.insert(2);
587 boost::intrusive::test::test_iterator_bidirectional< cont_int >(a);
588 if(boost::report_errors() != 0) {
589 return 1;
590 }
591 }
592 {
593 typedef boost::container::multiset<int> cont_int;
594 cont_int a; a.insert(0); a.insert(1); a.insert(2);
595 boost::intrusive::test::test_iterator_bidirectional< cont_int >(a);
596 if(boost::report_errors() != 0) {
597 return 1;
598 }
599 }
600
601 ////////////////////////////////////
602 // Node extraction/insertion testing functions
603 ////////////////////////////////////
604 if(!node_type_test())
605 return 1;
606
607 ////////////////////////////////////
608 // has_trivial_destructor_after_move testing
609 ////////////////////////////////////
610 // set, default allocator
611 {
612 typedef boost::container::set<int> cont;
613 typedef boost::container::dtl::tree<int, void, std::less<int>, void, void> tree;
614 if (boost::has_trivial_destructor_after_move<cont>::value !=
615 boost::has_trivial_destructor_after_move<tree>::value) {
616 std::cerr << "has_trivial_destructor_after_move(set, default allocator) test failed" << std::endl;
617 return 1;
618 }
619 }
620 // set, std::allocator
621 {
622 typedef boost::container::set<int, std::less<int>, std::allocator<int> > cont;
623 typedef boost::container::dtl::tree<int, void, std::less<int>, std::allocator<int>, void> tree;
624 if (boost::has_trivial_destructor_after_move<cont>::value !=
625 boost::has_trivial_destructor_after_move<tree>::value) {
626 std::cerr << "has_trivial_destructor_after_move(set, std::allocator) test failed" << std::endl;
627 return 1;
628 }
629 }
630 // multiset, default allocator
631 {
632 typedef boost::container::multiset<int> cont;
633 typedef boost::container::dtl::tree<int, void, std::less<int>, void, void> tree;
634 if (boost::has_trivial_destructor_after_move<cont>::value !=
635 boost::has_trivial_destructor_after_move<tree>::value) {
636 std::cerr << "has_trivial_destructor_after_move(multiset, default allocator) test failed" << std::endl;
637 return 1;
638 }
639 }
640 // multiset, std::allocator
641 {
642 typedef boost::container::multiset<int, std::less<int>, std::allocator<int> > cont;
643 typedef boost::container::dtl::tree<int, void, std::less<int>, std::allocator<int>, void> tree;
644 if (boost::has_trivial_destructor_after_move<cont>::value !=
645 boost::has_trivial_destructor_after_move<tree>::value) {
646 std::cerr << "has_trivial_destructor_after_move(multiset, std::allocator) test failed" << std::endl;
647 return 1;
648 }
649 }
650
651 return 0;
652 }
653