• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2016-2020 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     if(AlwaysEqual)BOOST_TEST(d2==d3);
92 
93     BOOST_TEST(p2.empty());
94     do_((BOOST_TEST(!p2.template is_registered<Types>()),0)...);
95 
96 #if !defined(BOOST_MSVC)&&\
97     BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB,BOOST_TESTED_AT(804))
98     /* Very odd behavior probably due to std::unordered_map allocator move
99      * ctor being implemented with move assignment, as reported in
100      * https://github.com/boostorg/poly_collection/issues/16
101      */
102 
103     if(!(Propagate&&!AlwaysEqual))
104 #endif
105     BOOST_TEST(p3.get_allocator().comes_from(root2));
106   }
107   {
108     rooted_poly_collection p2{root2};
109     p2=cp;
110     BOOST_TEST(p2==p);
111 
112 #if BOOST_WORKAROUND(BOOST_MSVC,<=1900)
113     /* std::unordered_map copy assignment does not propagate allocators */
114 
115     if(!Propagate)
116 #endif
117 #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900)
118     /* std::unordered_map copy assignment always and only propagates unequal
119      * allocators.
120      */
121 
122     if(!((Propagate&&AlwaysEqual)||(!Propagate&&!AlwaysEqual)))
123 #endif
124 #if !defined(BOOST_MSVC)&&\
125     BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB,BOOST_TESTED_AT(804))
126     /* std::unordered_map copy assignment does not propagate allocators, as
127      * reported in https://github.com/boostorg/poly_collection/issues/16
128      */
129 
130     if(!Propagate)
131 #endif
132     BOOST_TEST(p2.get_allocator().comes_from(Propagate?root1:root2));
133   }
134 #if BOOST_WORKAROUND(BOOST_MSVC,<=1900)
135   /* std::unordered_map move asignment does not propagate allocators */
136 
137   if(!Propagate&&AlwaysEqual)
138 #endif
139   {
140     rooted_poly_collection p2{cp};
141     auto                   d2=get_layout_data<Types...>(p2);
142     rooted_poly_collection p3{root2};
143     p3=std::move(p2);
144     auto                   d3=get_layout_data<Types...>(p3);
145     BOOST_TEST(p3==p);
146     if(Propagate||AlwaysEqual){
147       BOOST_TEST(d2==d3);
148       BOOST_TEST(p2.empty());
149       do_((BOOST_TEST(!p2.template is_registered<Types>()),0)...);
150     }
151 
152 #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900)
153     /* std::unordered_map move assignment always and only propagates unequal
154      * allocators.
155      */
156 
157     if(!((Propagate&&AlwaysEqual)||(!Propagate&&!AlwaysEqual)))
158 #endif
159 #if !defined(BOOST_MSVC)&&\
160     BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB,BOOST_TESTED_AT(804))
161     /* std::unordered_map move assignment does not propagate equal allocators,
162      * as reported in https://github.com/boostorg/poly_collection/issues/16
163      */
164 
165     if(!(Propagate&&AlwaysEqual))
166 #endif
167     BOOST_TEST(p3.get_allocator().comes_from(Propagate?root1:root2));
168   }
169 #if BOOST_WORKAROUND(BOOST_MSVC,<=1900)
170   /* std::unordered_map::swap does not correctly swap control information when
171    * swapping allocators, which causes crashes on "Checked Iterators" mode.
172    */
173 
174   if(!(Propagate&&!AlwaysEqual))
175 #endif
176   {
177     constexpr bool use_same_allocator=!Propagate&&!AlwaysEqual;
178 
179     rooted_poly_collection p2{cp},
180                            p3{use_same_allocator?root1:root2};
181 
182     auto d2=get_layout_data<Types...>(p2),
183          d3=get_layout_data<Types...>(p3);
184 
185     p2.swap(p3);
186     auto e2=get_layout_data<Types...>(p2),
187          e3=get_layout_data<Types...>(p3);
188     BOOST_TEST(d2==e3);
189     BOOST_TEST(d3==e2);
190     do_((BOOST_TEST(!p2.template is_registered<Types>()),0)...);
191     if(!use_same_allocator
192 #if BOOST_WORKAROUND(BOOST_MSVC,<=1900)
193        /* std::unordered_map::swap does not swap equal allocators */
194 
195        &&!(Propagate&&AlwaysEqual)
196 #endif
197 #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900)
198        /* std::unordered_map::swap always and only swaps unequal allocators */
199 
200        &&!((Propagate&&AlwaysEqual)||(!Propagate&&!AlwaysEqual))
201 #endif
202 #if !defined(BOOST_MSVC)&&\
203     BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB,BOOST_TESTED_AT(804))
204       /* std::unordered_map::swap does not swap equal allocators, as reported
205        * in https://github.com/boostorg/poly_collection/issues/16
206        */
207 
208       &&!(Propagate&&AlwaysEqual)
209 #endif
210     ){
211       BOOST_TEST(p2.get_allocator().comes_from(Propagate?root2:root1));
212       BOOST_TEST(p3.get_allocator().comes_from(Propagate?root1:root2));
213     }
214 
215     using std::swap;
216     swap(p2,p3);
217     auto f2=get_layout_data<Types...>(p2),
218          f3=get_layout_data<Types...>(p3);
219     BOOST_TEST(e2==f3);
220     BOOST_TEST(e3==f2);
221     do_((BOOST_TEST(!p3.template is_registered<Types>()),0)...);
222     if(!use_same_allocator){
223       BOOST_TEST(p2.get_allocator().comes_from(root1));
224       BOOST_TEST(p3.get_allocator().comes_from(root2));
225     }
226   }
227 }
228 
229 template<typename PolyCollection,typename ValueFactory,typename... Types>
test_construction()230 void test_construction()
231 {
232   {
233     constexpr bool propagate=true,always_equal=true;
234 
235     test_allocator_aware_construction<
236       !propagate,!always_equal,PolyCollection,ValueFactory,Types...>();
237     test_allocator_aware_construction<
238       !propagate, always_equal,PolyCollection,ValueFactory,Types...>();
239     test_allocator_aware_construction<
240        propagate,!always_equal,PolyCollection,ValueFactory,Types...>();
241     test_allocator_aware_construction<
242        propagate, always_equal,PolyCollection,ValueFactory,Types...>();
243   }
244 
245   {
246     PolyCollection        p;
247     const PolyCollection& cp=p;
248     ValueFactory          v;
249 
250     fill<
251       constraints<is_equality_comparable,is_copy_constructible>,
252       Types...
253     >(p,v,2);
254 
255     {
256       PolyCollection p2{cp.begin(),cp.end()};
257       BOOST_TEST(p2==p);
258     }
259     {
260       using type=first_of<
261          constraints<is_equality_comparable,is_copy_constructible>,
262          Types...>;
263 
264       PolyCollection p2{cp.template begin<type>(),cp.template end<type>()};
265       BOOST_TEST(
266         p2.size()==cp.template size<type>()&&
267         std::equal(
268           p2.template begin<type>(),p2.template end<type>(),
269           cp.template begin<type>()));
270     }
271   }
272 
273   {
274     using not_copy_constructible=
275       boost::poly_collection::not_copy_constructible;
276 
277     PolyCollection        p;
278     const PolyCollection& cp=p;
279     ValueFactory          v;
280 
281     fill<
282       constraints<is_equality_comparable,is_not_copy_constructible>,
283       Types...
284     >(p,v,2);
285 
286 #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900)
287     /* std::unordered_map copy construction and assigment crash when elements
288      * throw on copy construction.
289      */
290 
291     static_assert(
292       sizeof(not_copy_constructible)>0,""); /* Wunused-local-typedefs */
293     (void)cp;                               /* Wunused-variable       */
294 #else
295     check_throw<not_copy_constructible>([&]{
296       PolyCollection p2{cp};
297       (void)p2;
298     });
299     check_throw<not_copy_constructible>([&]{
300       PolyCollection p2;
301       p2=cp;
302     });
303 #endif
304 
305     {
306       PolyCollection p2{std::move(p)};
307       BOOST_TEST(!p2.empty());
308       BOOST_TEST(p.empty());
309       do_((BOOST_TEST(!p.template is_registered<Types>()),0)...);
310 
311       p={std::move(p2)};
312       BOOST_TEST(!p.empty());
313       BOOST_TEST(p2.empty());
314       do_((BOOST_TEST(!p2.template is_registered<Types>()),0)...);
315     }
316   }
317 }
318 
test_scoped_allocator()319 void test_scoped_allocator()
320 {
321 #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<50000)&&\
322     BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,>40704)
323   /* std::scoped_allocator_adaptor not assignable, see
324    * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65279 .
325    * The bug prevents poly_collection below from creating any segment.
326    */
327 #else
328   using vector_allocator=rooted_allocator<char>;
329   using vector=std::vector<char,vector_allocator>;
330   using concept_=boost::type_erasure::relaxed;
331   using element_allocator=rooted_allocator<
332     boost::poly_collection::any_collection_value_type<concept_>
333   >;
334   using collection_allocator=std::scoped_allocator_adaptor<
335     element_allocator,
336     vector_allocator
337    >;
338   using poly_collection=
339     boost::any_collection<concept_,collection_allocator>;
340 
341   element_allocator    roote{0};
342   vector_allocator     rootv{0};
343   collection_allocator al{roote,rootv};
344   poly_collection      p{al};
345 
346   p.emplace<vector>();
347   auto& s=*p.begin<vector>();
348   BOOST_TEST(p.get_allocator().comes_from(roote));
349 
350 #if BOOST_WORKAROUND(BOOST_MSVC,>=1910)&&BOOST_WORKAROUND(BOOST_MSVC,<1916)
351   /* https://developercommunity.visualstudio.com/content/problem/246251/
352    *   3136309.html
353    */
354 #else
355   BOOST_TEST(s.get_allocator().comes_from(rootv));
356 #endif
357 #endif
358 }
359 
test_construction()360 void test_construction()
361 {
362   test_construction<
363     any_types::collection,auto_increment,
364     any_types::t1,any_types::t2,any_types::t3,
365     any_types::t4,any_types::t5>();
366   test_construction<
367     base_types::collection,auto_increment,
368     base_types::t1,base_types::t2,base_types::t3,
369     base_types::t4,base_types::t5>();
370   test_construction<
371     function_types::collection,auto_increment,
372     function_types::t1,function_types::t2,function_types::t3,
373     function_types::t4,function_types::t5>();
374   test_scoped_allocator();
375 }
376