1 /* Copyright 2016-2019 Joaquin M Lopez Munoz.
2  * Distributed under the Boost Software License, Version 1.0.
3  * (See accompanying file LICENSE_1_0.txt or copy at
4  * http://www.boost.org/LICENSE_1_0.txt)
5  *
6  * See http://www.boost.org/libs/poly_collection for library home page.
7  */
8 
9 #include "test_construction.hpp"
10 
11 #include <algorithm>
12 #include <boost/config.hpp>
13 #include <boost/core/lightweight_test.hpp>
14 #include <boost/detail/workaround.hpp>
15 #include <boost/type_erasure/relaxed.hpp>
16 #include <scoped_allocator>
17 #include <utility>
18 #include <vector>
19 #include "any_types.hpp"
20 #include "base_types.hpp"
21 #include "function_types.hpp"
22 #include "test_utilities.hpp"
23 
24 using namespace test_utilities;
25 
26 template<
27   bool Propagate,bool AlwaysEqual,
28   typename PolyCollection,typename ValueFactory,typename... Types
29 >
test_allocator_aware_construction()30 void test_allocator_aware_construction()
31 {
32   using rooted_poly_collection=realloc_poly_collection<
33     PolyCollection,rooted_allocator,
34     std::integral_constant<bool,Propagate>,
35     std::integral_constant<bool,AlwaysEqual>>;
36   using allocator_type=typename rooted_poly_collection::allocator_type;
37 
38   allocator_type                root1{0},root2{0};
39   rooted_poly_collection        p{root1};
40   const rooted_poly_collection& cp=p;
41   ValueFactory                  v;
42 
43   fill<
44     constraints<is_equality_comparable,is_copy_constructible>,
45     Types...
46   >(p,v,2);
47 
48   {
49     rooted_poly_collection p2{cp};
50     BOOST_TEST(p2==p);
51     BOOST_TEST(p2.get_allocator().comes_from(root1));
52   }
53   {
54     rooted_poly_collection p2{cp};
55     auto                   d2=get_layout_data<Types...>(p2);
56     rooted_poly_collection p3{std::move(p2)};
57     auto                   d3=get_layout_data<Types...>(p3);
58     BOOST_TEST(p3==p);
59     BOOST_TEST(d2==d3);
60     BOOST_TEST(p2.empty());
61     do_((BOOST_TEST(!p2.template is_registered<Types>()),0)...);
62     BOOST_TEST(p2.get_allocator().comes_from(root1));
63   }
64   {
65     rooted_poly_collection p2{cp,root2};
66     BOOST_TEST(p2==p);
67     BOOST_TEST(p2.get_allocator().comes_from(root2));
68   }
69 #if BOOST_WORKAROUND(BOOST_MSVC,<=1900)
70   /* std::unordered_map allocator move ctor does not work when source and
71    * and target allocators are not equal.
72    */
73 
74   if(AlwaysEqual)
75 #endif
76 #if BOOST_WORKAROUND(_MSVC_STL_UPDATE,==201811L)
77   /* This particular version of VS2019 has a bug in std::unordered_map
78    * allocator move ctor when source and target allocators are not equal.
79    * After private communication from Billy O'Neal.
80    */
81 
82   if(AlwaysEqual)
83 #endif
84   {
85     rooted_poly_collection p2{cp};
86     auto                   d2=get_layout_data<Types...>(p2);
87     rooted_poly_collection p3{std::move(p2),root2};
88     auto                   d3=get_layout_data<Types...>(p3);
89 
90     BOOST_TEST(p3==p);
91 
92 #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900)
93     /* Limitations from libstdc++-v3 force move construction with allocator
94      * to decay to copy construction with allocator.
95      */
96 
97     (void)(d2==d3); /* Wunused-variable */
98 #else
99     if(AlwaysEqual)BOOST_TEST(d2==d3);
100 #endif
101 
102     BOOST_TEST(p2.empty());
103     do_((BOOST_TEST(!p2.template is_registered<Types>()),0)...);
104 
105 #if !defined(BOOST_MSVC)&&\
106     BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB,BOOST_TESTED_AT(804))
107     /* Very odd behavior probably due to std::unordered_map allocator move
108      * ctor being implemented with move assignment, as reported in
109      * https://github.com/boostorg/poly_collection/issues/16
110      */
111 
112     if(!(Propagate&&!AlwaysEqual))
113 #endif
114     BOOST_TEST(p3.get_allocator().comes_from(root2));
115   }
116   {
117     rooted_poly_collection p2{root2};
118     p2=cp;
119     BOOST_TEST(p2==p);
120 
121 #if BOOST_WORKAROUND(BOOST_MSVC,<=1900)
122     /* std::unordered_map copy assignment does not propagate allocators */
123 
124     if(!Propagate)
125 #endif
126 #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900)
127     /* std::unordered_map copy assignment always and only propagates unequal
128      * allocators.
129      */
130 
131     if(!((Propagate&&AlwaysEqual)||(!Propagate&&!AlwaysEqual)))
132 #endif
133 #if !defined(BOOST_MSVC)&&\
134     BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB,BOOST_TESTED_AT(804))
135     /* std::unordered_map copy assignment does not propagate allocators, as
136      * reported in https://github.com/boostorg/poly_collection/issues/16
137      */
138 
139     if(!Propagate)
140 #endif
141     BOOST_TEST(p2.get_allocator().comes_from(Propagate?root1:root2));
142   }
143 #if BOOST_WORKAROUND(BOOST_MSVC,<=1900)
144   /* std::unordered_map move asignment does not propagate allocators */
145 
146   if(!Propagate&&AlwaysEqual)
147 #endif
148   {
149     rooted_poly_collection p2{cp};
150     auto                   d2=get_layout_data<Types...>(p2);
151     rooted_poly_collection p3{root2};
152     p3=std::move(p2);
153     auto                   d3=get_layout_data<Types...>(p3);
154     BOOST_TEST(p3==p);
155     if(Propagate||AlwaysEqual){
156       BOOST_TEST(d2==d3);
157       BOOST_TEST(p2.empty());
158       do_((BOOST_TEST(!p2.template is_registered<Types>()),0)...);
159     }
160 
161 #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900)
162     /* std::unordered_map move assignment always and only propagates unequal
163      * allocators.
164      */
165 
166     if(!((Propagate&&AlwaysEqual)||(!Propagate&&!AlwaysEqual)))
167 #endif
168 #if !defined(BOOST_MSVC)&&\
169     BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB,BOOST_TESTED_AT(804))
170     /* std::unordered_map move assignment does not propagate equal allocators,
171      * as reported in https://github.com/boostorg/poly_collection/issues/16
172      */
173 
174     if(!(Propagate&&AlwaysEqual))
175 #endif
176     BOOST_TEST(p3.get_allocator().comes_from(Propagate?root1:root2));
177   }
178 #if BOOST_WORKAROUND(BOOST_MSVC,<=1900)
179   /* std::unordered_map::swap does not correctly swap control information when
180    * swapping allocators, which causes crashes on "Checked Iterators" mode.
181    */
182 
183   if(!(Propagate&&!AlwaysEqual))
184 #endif
185   {
186     constexpr bool use_same_allocator=!Propagate&&!AlwaysEqual;
187 
188     rooted_poly_collection p2{cp},
189                            p3{use_same_allocator?root1:root2};
190 
191     auto d2=get_layout_data<Types...>(p2),
192          d3=get_layout_data<Types...>(p3);
193 
194     p2.swap(p3);
195     auto e2=get_layout_data<Types...>(p2),
196          e3=get_layout_data<Types...>(p3);
197     BOOST_TEST(d2==e3);
198     BOOST_TEST(d3==e2);
199     do_((BOOST_TEST(!p2.template is_registered<Types>()),0)...);
200     if(!use_same_allocator
201 #if BOOST_WORKAROUND(BOOST_MSVC,<=1900)
202        /* std::unordered_map::swap does not swap equal allocators */
203 
204        &&!(Propagate&&AlwaysEqual)
205 #endif
206 #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900)
207        /* std::unordered_map::swap always and only swaps unequal allocators */
208 
209        &&!((Propagate&&AlwaysEqual)||(!Propagate&&!AlwaysEqual))
210 #endif
211 #if !defined(BOOST_MSVC)&&\
212     BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB,BOOST_TESTED_AT(804))
213       /* std::unordered_map::swap does not swap equal allocators, as reported
214        * in https://github.com/boostorg/poly_collection/issues/16
215        */
216 
217       &&!(Propagate&&AlwaysEqual)
218 #endif
219     ){
220       BOOST_TEST(p2.get_allocator().comes_from(Propagate?root2:root1));
221       BOOST_TEST(p3.get_allocator().comes_from(Propagate?root1:root2));
222     }
223 
224     using std::swap;
225     swap(p2,p3);
226     auto f2=get_layout_data<Types...>(p2),
227          f3=get_layout_data<Types...>(p3);
228     BOOST_TEST(e2==f3);
229     BOOST_TEST(e3==f2);
230     do_((BOOST_TEST(!p3.template is_registered<Types>()),0)...);
231     if(!use_same_allocator){
232       BOOST_TEST(p2.get_allocator().comes_from(root1));
233       BOOST_TEST(p3.get_allocator().comes_from(root2));
234     }
235   }
236 }
237 
238 template<typename PolyCollection,typename ValueFactory,typename... Types>
test_construction()239 void test_construction()
240 {
241   {
242     constexpr bool propagate=true,always_equal=true;
243 
244     test_allocator_aware_construction<
245       !propagate,!always_equal,PolyCollection,ValueFactory,Types...>();
246     test_allocator_aware_construction<
247       !propagate, always_equal,PolyCollection,ValueFactory,Types...>();
248     test_allocator_aware_construction<
249        propagate,!always_equal,PolyCollection,ValueFactory,Types...>();
250     test_allocator_aware_construction<
251        propagate, always_equal,PolyCollection,ValueFactory,Types...>();
252   }
253 
254   {
255     PolyCollection        p;
256     const PolyCollection& cp=p;
257     ValueFactory          v;
258 
259     fill<
260       constraints<is_equality_comparable,is_copy_constructible>,
261       Types...
262     >(p,v,2);
263 
264     {
265       PolyCollection p2{cp.begin(),cp.end()};
266       BOOST_TEST(p2==p);
267     }
268     {
269       using type=first_of<
270          constraints<is_equality_comparable,is_copy_constructible>,
271          Types...>;
272 
273       PolyCollection p2{cp.template begin<type>(),cp.template end<type>()};
274       BOOST_TEST(
275         p2.size()==cp.template size<type>()&&
276         std::equal(
277           p2.template begin<type>(),p2.template end<type>(),
278           cp.template begin<type>()));
279     }
280   }
281 
282   {
283     using not_copy_constructible=
284       boost::poly_collection::not_copy_constructible;
285 
286     PolyCollection        p;
287     const PolyCollection& cp=p;
288     ValueFactory          v;
289 
290     fill<
291       constraints<is_equality_comparable,is_not_copy_constructible>,
292       Types...
293     >(p,v,2);
294 
295 #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900)
296     /* std::unordered_map copy construction and assigment crash when elements
297      * throw on copy construction.
298      */
299 
300     static_assert(
301       sizeof(not_copy_constructible)>0,""); /* Wunused-local-typedefs */
302     (void)cp;                               /* Wunused-variable       */
303 #else
304     check_throw<not_copy_constructible>([&]{
305       PolyCollection p2{cp};
306       (void)p2;
307     });
308     check_throw<not_copy_constructible>([&]{
309       PolyCollection p2;
310       p2=cp;
311     });
312 #endif
313 
314     {
315       PolyCollection p2{std::move(p)};
316       BOOST_TEST(!p2.empty());
317       BOOST_TEST(p.empty());
318       do_((BOOST_TEST(!p.template is_registered<Types>()),0)...);
319 
320       p={std::move(p2)};
321       BOOST_TEST(!p.empty());
322       BOOST_TEST(p2.empty());
323       do_((BOOST_TEST(!p2.template is_registered<Types>()),0)...);
324     }
325   }
326 }
327 
test_scoped_allocator()328 void test_scoped_allocator()
329 {
330 #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<50000)&&\
331     BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,>40704)
332   /* std::scoped_allocator_adaptor not assignable, see
333    * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65279 .
334    * The bug prevents poly_collection below from creating any segment.
335    */
336 #else
337   using vector_allocator=rooted_allocator<char>;
338   using vector=std::vector<char,vector_allocator>;
339   using concept_=boost::type_erasure::relaxed;
340   using element_allocator=rooted_allocator<
341     boost::poly_collection::any_collection_value_type<concept_>
342   >;
343   using collection_allocator=std::scoped_allocator_adaptor<
344     element_allocator,
345     vector_allocator
346    >;
347   using poly_collection=
348     boost::any_collection<concept_,collection_allocator>;
349 
350   element_allocator    roote{0};
351   vector_allocator     rootv{0};
352   collection_allocator al{roote,rootv};
353   poly_collection      p{al};
354 
355   p.emplace<vector>();
356   auto& s=*p.begin<vector>();
357   BOOST_TEST(p.get_allocator().comes_from(roote));
358 
359 #if BOOST_WORKAROUND(BOOST_MSVC,>=1910)&&BOOST_WORKAROUND(BOOST_MSVC,<1916)
360   /* https://developercommunity.visualstudio.com/content/problem/246251/
361    *   3136309.html
362    */
363 #else
364   BOOST_TEST(s.get_allocator().comes_from(rootv));
365 #endif
366 #endif
367 }
368 
test_construction()369 void test_construction()
370 {
371   test_construction<
372     any_types::collection,auto_increment,
373     any_types::t1,any_types::t2,any_types::t3,
374     any_types::t4,any_types::t5>();
375   test_construction<
376     base_types::collection,auto_increment,
377     base_types::t1,base_types::t2,base_types::t3,
378     base_types::t4,base_types::t5>();
379   test_construction<
380     function_types::collection,auto_increment,
381     function_types::t1,function_types::t2,function_types::t3,
382     function_types::t4,function_types::t5>();
383   test_scoped_allocator();
384 }
385