1 /* Boost.MultiIndex test for allocator awareness.
2  *
3  * Copyright 2003-2020 Joaquin M Lopez Munoz.
4  * Distributed under the Boost Software License, Version 1.0.
5  * (See accompanying file LICENSE_1_0.txt or copy at
6  * http://www.boost.org/LICENSE_1_0.txt)
7  *
8  * See http://www.boost.org/libs/multi_index for library home page.
9  */
10 
11 #include "test_alloc_awareness.hpp"
12 
13 #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
14 #include <boost/detail/lightweight_test.hpp>
15 #include <boost/move/core.hpp>
16 #include <boost/move/utility_core.hpp>
17 #include "pre_multi_index.hpp"
18 #include <boost/multi_index_container.hpp>
19 #include <boost/multi_index/hashed_index.hpp>
20 #include <boost/multi_index/member.hpp>
21 #include <boost/multi_index/ordered_index.hpp>
22 #include <boost/multi_index/random_access_index.hpp>
23 #include <boost/multi_index/ranked_index.hpp>
24 #include <boost/multi_index/sequenced_index.hpp>
25 #include "rooted_allocator.hpp"
26 
27 struct move_tracker
28 {
move_trackermove_tracker29   move_tracker(int n):n(n),move_cted(false){}
move_trackermove_tracker30   move_tracker(const move_tracker& x):n(x.n),move_cted(false){}
move_trackermove_tracker31   move_tracker(BOOST_RV_REF(move_tracker) x):n(x.n),move_cted(true){}
operator =move_tracker32   move_tracker& operator=(BOOST_COPY_ASSIGN_REF(move_tracker) x)
33     {n=x.n;return *this;}
operator =move_tracker34   move_tracker& operator=(BOOST_RV_REF(move_tracker) x){n=x.n;return *this;}
35 
36   int  n;
37   bool move_cted;
38 
39 private:
40   BOOST_COPYABLE_AND_MOVABLE(move_tracker)
41 };
42 
operator ==(const move_tracker & x,const move_tracker & y)43 inline bool operator==(const move_tracker& x,const move_tracker& y)
44 {
45   return x.n==y.n;
46 }
47 
operator <(const move_tracker & x,const move_tracker & y)48 inline bool operator<(const move_tracker& x,const move_tracker& y)
49 {
50   return x.n<y.n;
51 }
52 
53 #if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
54 namespace boost{
55 #endif
56 
hash_value(const move_tracker & x)57 inline std::size_t hash_value(const move_tracker& x)
58 {
59   boost::hash<int> h;
60   return h(x.n);
61 }
62 
63 #if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
64 } /* namespace boost */
65 #endif
66 
67 #if defined(BOOST_NO_CXX17_IF_CONSTEXPR)&&defined(BOOST_MSVC)
68 #pragma warning(push)
69 #pragma warning(disable:4127) /* conditional expression is constant */
70 #endif
71 
72 template<bool Propagate,bool AlwaysEqual>
test_allocator_awareness_for()73 void test_allocator_awareness_for()
74 {
75   using namespace boost::multi_index;
76 
77   typedef rooted_allocator<move_tracker,Propagate,AlwaysEqual> allocator;
78   typedef multi_index_container<
79     move_tracker,
80     indexed_by<
81       hashed_unique<identity<move_tracker> >,
82       ordered_unique<identity<move_tracker> >,
83       random_access<>,
84       ranked_unique<identity<move_tracker> >,
85       sequenced<>
86     >,
87     allocator
88   >                                                            container;
89 
90   allocator root1(0),root2(0);
91   container c(root1);
92   for(int i=0;i<10;++i)c.emplace(i);
93 
94   BOOST_TEST(c.get_allocator().comes_from(root1));
95 
96   {
97     container c2(c,root2);
98     BOOST_TEST(c2.get_allocator().comes_from(root2));
99     BOOST_TEST(c2==c);
100   }
101   {
102     container           c2(c);
103     const move_tracker* pfirst=&*c2.begin();
104     container           c3(boost::move(c2),root2);
105     BOOST_TEST(c3.get_allocator().comes_from(root2));
106     BOOST_TEST(c3==c);
107     BOOST_TEST(c2.empty());
108     BOOST_TEST(AlwaysEqual==(&*c3.begin()==pfirst));
109     BOOST_TEST(!AlwaysEqual==(c3.begin()->move_cted));
110   }
111   {
112     container c2(root2);
113     c2=c;
114     BOOST_TEST(c2.get_allocator().comes_from(Propagate?root1:root2));
115     BOOST_TEST(c2==c);
116   }
117   {
118     const bool          element_transfer=Propagate||AlwaysEqual;
119 
120     container           c2(c);
121     const move_tracker* pfirst=&*c2.begin();
122     container           c3(root2);
123     c3=boost::move(c2);
124     BOOST_TEST(c3.get_allocator().comes_from(Propagate?root1:root2));
125     BOOST_TEST(c3==c);
126     BOOST_TEST(c2.empty());
127     BOOST_TEST(element_transfer==(&*c3.begin()==pfirst));
128     BOOST_TEST(!element_transfer==(c3.begin()->move_cted));
129   }
130   if(Propagate||AlwaysEqual){
131     container           c2(c);
132     const move_tracker* pfirst=&*c2.begin();
133     container           c3(root2);
134     c3.swap(c2);
135     BOOST_TEST(c2.get_allocator().comes_from(Propagate?root2:root1));
136     BOOST_TEST(c3.get_allocator().comes_from(Propagate?root1:root2));
137     BOOST_TEST(c3==c);
138     BOOST_TEST(c2.empty());
139     BOOST_TEST(&*c3.begin()==pfirst);
140     BOOST_TEST(!c3.begin()->move_cted);
141   }
142 }
143 
144 #if defined(BOOST_NO_CXX17_IF_CONSTEXPR)&&defined(BOOST_MSVC)
145 #pragma warning(pop) /* C4127 */
146 #endif
147 
test_allocator_awareness()148 void test_allocator_awareness()
149 {
150   test_allocator_awareness_for<false,false>();
151   test_allocator_awareness_for<false,true>();
152 
153 #if !defined(BOOST_NO_CXX11_ALLOCATOR)
154   /* only in C+11 onwards are allocators potentially expected to propagate */
155 
156   test_allocator_awareness_for<true,false>();
157   test_allocator_awareness_for<true,true>();
158 
159 #endif
160 }
161