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 | > |
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> |
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 | |
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 | |
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 | |