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
24using namespace test_utilities;
25
26template<
27 bool Propagate,bool AlwaysEqual,
28 typename PolyCollection,typename ValueFactory,typename... Types
29>
30void 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
229template<typename PolyCollection,typename ValueFactory,typename... Types>
230void 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
319void 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
360void 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

source code of boost/libs/poly_collection/test/test_construction.cpp