/* Copyright 2016-2019 Joaquin M Lopez Munoz. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * See http://www.boost.org/libs/poly_collection for library home page. */ #include "test_construction.hpp" #include #include #include #include #include #include #include #include #include "any_types.hpp" #include "base_types.hpp" #include "function_types.hpp" #include "test_utilities.hpp" using namespace test_utilities; template< bool Propagate,bool AlwaysEqual, typename PolyCollection,typename ValueFactory,typename... Types > void test_allocator_aware_construction() { using rooted_poly_collection=realloc_poly_collection< PolyCollection,rooted_allocator, std::integral_constant, std::integral_constant>; using allocator_type=typename rooted_poly_collection::allocator_type; allocator_type root1{0},root2{0}; rooted_poly_collection p{root1}; const rooted_poly_collection& cp=p; ValueFactory v; fill< constraints, Types... >(p,v,2); { rooted_poly_collection p2{cp}; BOOST_TEST(p2==p); BOOST_TEST(p2.get_allocator().comes_from(root1)); } { rooted_poly_collection p2{cp}; auto d2=get_layout_data(p2); rooted_poly_collection p3{std::move(p2)}; auto d3=get_layout_data(p3); BOOST_TEST(p3==p); BOOST_TEST(d2==d3); BOOST_TEST(p2.empty()); do_((BOOST_TEST(!p2.template is_registered()),0)...); BOOST_TEST(p2.get_allocator().comes_from(root1)); } { rooted_poly_collection p2{cp,root2}; BOOST_TEST(p2==p); BOOST_TEST(p2.get_allocator().comes_from(root2)); } #if BOOST_WORKAROUND(BOOST_MSVC,<=1900) /* std::unordered_map allocator move ctor does not work when source and * and target allocators are not equal. */ if(AlwaysEqual) #endif #if BOOST_WORKAROUND(_MSVC_STL_UPDATE,==201811L) /* This particular version of VS2019 has a bug in std::unordered_map * allocator move ctor when source and target allocators are not equal. * After private communication from Billy O'Neal. */ if(AlwaysEqual) #endif { rooted_poly_collection p2{cp}; auto d2=get_layout_data(p2); rooted_poly_collection p3{std::move(p2),root2}; auto d3=get_layout_data(p3); BOOST_TEST(p3==p); #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900) /* Limitations from libstdc++-v3 force move construction with allocator * to decay to copy construction with allocator. */ (void)(d2==d3); /* Wunused-variable */ #else if(AlwaysEqual)BOOST_TEST(d2==d3); #endif BOOST_TEST(p2.empty()); do_((BOOST_TEST(!p2.template is_registered()),0)...); #if !defined(BOOST_MSVC)&&\ BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB,BOOST_TESTED_AT(804)) /* Very odd behavior probably due to std::unordered_map allocator move * ctor being implemented with move assignment, as reported in * https://github.com/boostorg/poly_collection/issues/16 */ if(!(Propagate&&!AlwaysEqual)) #endif BOOST_TEST(p3.get_allocator().comes_from(root2)); } { rooted_poly_collection p2{root2}; p2=cp; BOOST_TEST(p2==p); #if BOOST_WORKAROUND(BOOST_MSVC,<=1900) /* std::unordered_map copy assignment does not propagate allocators */ if(!Propagate) #endif #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900) /* std::unordered_map copy assignment always and only propagates unequal * allocators. */ if(!((Propagate&&AlwaysEqual)||(!Propagate&&!AlwaysEqual))) #endif #if !defined(BOOST_MSVC)&&\ BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB,BOOST_TESTED_AT(804)) /* std::unordered_map copy assignment does not propagate allocators, as * reported in https://github.com/boostorg/poly_collection/issues/16 */ if(!Propagate) #endif BOOST_TEST(p2.get_allocator().comes_from(Propagate?root1:root2)); } #if BOOST_WORKAROUND(BOOST_MSVC,<=1900) /* std::unordered_map move asignment does not propagate allocators */ if(!Propagate&&AlwaysEqual) #endif { rooted_poly_collection p2{cp}; auto d2=get_layout_data(p2); rooted_poly_collection p3{root2}; p3=std::move(p2); auto d3=get_layout_data(p3); BOOST_TEST(p3==p); if(Propagate||AlwaysEqual){ BOOST_TEST(d2==d3); BOOST_TEST(p2.empty()); do_((BOOST_TEST(!p2.template is_registered()),0)...); } #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900) /* std::unordered_map move assignment always and only propagates unequal * allocators. */ if(!((Propagate&&AlwaysEqual)||(!Propagate&&!AlwaysEqual))) #endif #if !defined(BOOST_MSVC)&&\ BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB,BOOST_TESTED_AT(804)) /* std::unordered_map move assignment does not propagate equal allocators, * as reported in https://github.com/boostorg/poly_collection/issues/16 */ if(!(Propagate&&AlwaysEqual)) #endif BOOST_TEST(p3.get_allocator().comes_from(Propagate?root1:root2)); } #if BOOST_WORKAROUND(BOOST_MSVC,<=1900) /* std::unordered_map::swap does not correctly swap control information when * swapping allocators, which causes crashes on "Checked Iterators" mode. */ if(!(Propagate&&!AlwaysEqual)) #endif { constexpr bool use_same_allocator=!Propagate&&!AlwaysEqual; rooted_poly_collection p2{cp}, p3{use_same_allocator?root1:root2}; auto d2=get_layout_data(p2), d3=get_layout_data(p3); p2.swap(p3); auto e2=get_layout_data(p2), e3=get_layout_data(p3); BOOST_TEST(d2==e3); BOOST_TEST(d3==e2); do_((BOOST_TEST(!p2.template is_registered()),0)...); if(!use_same_allocator #if BOOST_WORKAROUND(BOOST_MSVC,<=1900) /* std::unordered_map::swap does not swap equal allocators */ &&!(Propagate&&AlwaysEqual) #endif #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900) /* std::unordered_map::swap always and only swaps unequal allocators */ &&!((Propagate&&AlwaysEqual)||(!Propagate&&!AlwaysEqual)) #endif #if !defined(BOOST_MSVC)&&\ BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB,BOOST_TESTED_AT(804)) /* std::unordered_map::swap does not swap equal allocators, as reported * in https://github.com/boostorg/poly_collection/issues/16 */ &&!(Propagate&&AlwaysEqual) #endif ){ BOOST_TEST(p2.get_allocator().comes_from(Propagate?root2:root1)); BOOST_TEST(p3.get_allocator().comes_from(Propagate?root1:root2)); } using std::swap; swap(p2,p3); auto f2=get_layout_data(p2), f3=get_layout_data(p3); BOOST_TEST(e2==f3); BOOST_TEST(e3==f2); do_((BOOST_TEST(!p3.template is_registered()),0)...); if(!use_same_allocator){ BOOST_TEST(p2.get_allocator().comes_from(root1)); BOOST_TEST(p3.get_allocator().comes_from(root2)); } } } template void test_construction() { { constexpr bool propagate=true,always_equal=true; test_allocator_aware_construction< !propagate,!always_equal,PolyCollection,ValueFactory,Types...>(); test_allocator_aware_construction< !propagate, always_equal,PolyCollection,ValueFactory,Types...>(); test_allocator_aware_construction< propagate,!always_equal,PolyCollection,ValueFactory,Types...>(); test_allocator_aware_construction< propagate, always_equal,PolyCollection,ValueFactory,Types...>(); } { PolyCollection p; const PolyCollection& cp=p; ValueFactory v; fill< constraints, Types... >(p,v,2); { PolyCollection p2{cp.begin(),cp.end()}; BOOST_TEST(p2==p); } { using type=first_of< constraints, Types...>; PolyCollection p2{cp.template begin(),cp.template end()}; BOOST_TEST( p2.size()==cp.template size()&& std::equal( p2.template begin(),p2.template end(), cp.template begin())); } } { using not_copy_constructible= boost::poly_collection::not_copy_constructible; PolyCollection p; const PolyCollection& cp=p; ValueFactory v; fill< constraints, Types... >(p,v,2); #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900) /* std::unordered_map copy construction and assigment crash when elements * throw on copy construction. */ static_assert( sizeof(not_copy_constructible)>0,""); /* Wunused-local-typedefs */ (void)cp; /* Wunused-variable */ #else check_throw([&]{ PolyCollection p2{cp}; (void)p2; }); check_throw([&]{ PolyCollection p2; p2=cp; }); #endif { PolyCollection p2{std::move(p)}; BOOST_TEST(!p2.empty()); BOOST_TEST(p.empty()); do_((BOOST_TEST(!p.template is_registered()),0)...); p={std::move(p2)}; BOOST_TEST(!p.empty()); BOOST_TEST(p2.empty()); do_((BOOST_TEST(!p2.template is_registered()),0)...); } } } void test_scoped_allocator() { #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<50000)&&\ BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,>40704) /* std::scoped_allocator_adaptor not assignable, see * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65279 . * The bug prevents poly_collection below from creating any segment. */ #else using vector_allocator=rooted_allocator; using vector=std::vector; using concept_=boost::type_erasure::relaxed; using element_allocator=rooted_allocator< boost::poly_collection::any_collection_value_type >; using collection_allocator=std::scoped_allocator_adaptor< element_allocator, vector_allocator >; using poly_collection= boost::any_collection; element_allocator roote{0}; vector_allocator rootv{0}; collection_allocator al{roote,rootv}; poly_collection p{al}; p.emplace(); auto& s=*p.begin(); BOOST_TEST(p.get_allocator().comes_from(roote)); #if BOOST_WORKAROUND(BOOST_MSVC,>=1910)&&BOOST_WORKAROUND(BOOST_MSVC,<1916) /* https://developercommunity.visualstudio.com/content/problem/246251/ * 3136309.html */ #else BOOST_TEST(s.get_allocator().comes_from(rootv)); #endif #endif } void test_construction() { test_construction< any_types::collection,auto_increment, any_types::t1,any_types::t2,any_types::t3, any_types::t4,any_types::t5>(); test_construction< base_types::collection,auto_increment, base_types::t1,base_types::t2,base_types::t3, base_types::t4,base_types::t5>(); test_construction< function_types::collection,auto_increment, function_types::t1,function_types::t2,function_types::t3, function_types::t4,function_types::t5>(); test_scoped_allocator(); }