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