/* Copyright 2018 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. */ #ifndef BOOST_POLY_COLLECTION_DETAIL_ALLOCATOR_ADAPTOR_HPP #define BOOST_POLY_COLLECTION_DETAIL_ALLOCATOR_ADAPTOR_HPP #if defined(_MSC_VER) #pragma once #endif #include #include #include #include #include #include #include #include namespace boost{ namespace poly_collection{ namespace detail{ /* [container.requirements.general]/3 state that containers must use the * allocator's construct/destroy member functions to construct/destroy * elements and *not at all* for any other type. Since poly_collection is * implemented as a multi-level structure of container and container-like * objects, we need to use an adaptor for the user-provided Allocator that * prevents intermediate entities from calling Allocator::[construct|destroy]. * allocator_adaptor does this by taking advantage of the fact that * elements are ultimately held within a value_holder: * - construct(value_holder*,...) uses placement new construction and * passes the wrapped Allocator object for value_holder to use for * its inner construction of T. * - For the rest of types, construct uses placement new construction and * passes down the adaptor object itself as an argument following an * approach analogous to that of std::scoped_allocator_adaptor. * - destroy(value_holder) resorts to Allocator::destroy to destroy the * contained T element. * - For the rest of types, destroy(T) calls ~T directly. * * Code has been ripped and adapted from libc++'s implementation of * std::scoped_allocator_adaptor. */ template class value_holder_base; template class value_holder; template struct uses_alloc_ctor_impl { using RawAllocator=typename std::remove_cv< typename std::remove_reference::type >::type; static const bool ua=std::uses_allocator::value; static const int ic=is_constructible< T,std::allocator_arg_t,Allocator,Args...>::value?1:0; static const int value=ua?2-ic:0; }; template struct uses_alloc_ctor: std::integral_constant::value> {}; template struct allocator_is_always_equal:std::is_empty{}; template struct allocator_is_always_equal< Allocator, mp11::mp_void< typename std::allocator_traits::is_always_equal > >:std::allocator_traits::is_always_equal{}; template struct allocator_adaptor:Allocator { using traits=std::allocator_traits; using value_type=typename traits::value_type; using size_type=typename traits::size_type; using difference_type=typename traits::difference_type; using pointer=typename traits::pointer; using const_pointer=typename traits::const_pointer; using void_pointer=typename traits::void_pointer; using const_void_pointer=typename traits::const_void_pointer; using propagate_on_container_copy_assignment= typename traits::propagate_on_container_copy_assignment; using propagate_on_container_move_assignment= typename traits::propagate_on_container_move_assignment; using propagate_on_container_swap= typename traits::propagate_on_container_swap; using is_always_equal=typename allocator_is_always_equal::type; template struct rebind { using other=allocator_adaptor>; }; allocator_adaptor()=default; allocator_adaptor(const allocator_adaptor&)=default; template< typename Allocator2, typename std::enable_if< is_constructible::value >::type* =nullptr > allocator_adaptor(const Allocator2& x)noexcept:Allocator{x}{} template< typename Allocator2, typename std::enable_if< is_constructible::value >::type* =nullptr > allocator_adaptor(const allocator_adaptor& x)noexcept: Allocator{x.allocator()}{} allocator_adaptor& operator=(const allocator_adaptor&)=default; Allocator& allocator()noexcept{return *this;} const Allocator& allocator()const noexcept{return *this;} template void construct(T* p,Args&&... args) { construct_( uses_alloc_ctor{}, p,std::forward(args)...); } template void construct(value_holder* p,Args&&... args) { ::new ((void*)p) value_holder(allocator(),std::forward(args)...); } template void construct( std::pair* p,std::piecewise_construct_t, std::tuple x,std::tuple y) { ::new ((void*)p) std::pair( std::piecewise_construct, transform_tuple( uses_alloc_ctor{}, std::move(x), mp11::make_index_sequence{}), transform_tuple( uses_alloc_ctor{}, std::move(y), mp11::make_index_sequence{}) ); } template void construct(std::pair* p) { construct(p,std::piecewise_construct,std::tuple<>{},std::tuple<>{}); } template void construct(std::pair* p,U&& x,V&& y) { construct( p,std::piecewise_construct, std::forward_as_tuple(std::forward(x)), std::forward_as_tuple(std::forward(y))); } template void construct(std::pair* p,const std::pair& x) { construct( p,std::piecewise_construct, std::forward_as_tuple(x.first),std::forward_as_tuple(x.second)); } template void construct(std::pair* p,std::pair&& x) { construct( p,std::piecewise_construct, std::forward_as_tuple(std::forward(x.first)), std::forward_as_tuple(std::forward(x.second))); } template void destroy(T* p) { p->~T(); } template void destroy(value_holder* p) { traits::destroy( allocator(), reinterpret_cast(static_cast*>(p))); } allocator_adaptor select_on_container_copy_construction()const noexcept { return traits::select_on_container_copy_construction(allocator()); } private: template void construct_( std::integral_constant, /* doesn't use allocator */ T* p,Args&&... args) { ::new ((void*)p) T(std::forward(args)...); } template void construct_( std::integral_constant, /* with std::allocator_arg */ T* p,Args&&... args) { ::new ((void*)p) T(std::allocator_arg,*this,std::forward(args)...); } template void construct_( std::integral_constant, /* allocator at the end */ T* p,Args&&... args) { ::new ((void*)p) T(std::forward(args)...,*this); } template std::tuple transform_tuple( std::integral_constant, /* doesn't use allocator */ std::tuple&& t,mp11::index_sequence) { return std::tuple(std::get(std::move(t))...); } template std::tuple transform_tuple( std::integral_constant, /* with std::allocator_arg */ std::tuple&& t,mp11::index_sequence) { return std::tuple< std::allocator_arg_t,allocator_adaptor&,Args&&...>( std::allocator_arg,*this,std::get(std::move(t))...); } template std::tuple transform_tuple( std::integral_constant, /* allocator at the end */ std::tuple&& t,mp11::index_sequence) { return std::tuple( std::get(std::move(t))...,*this); } }; template bool operator==( const allocator_adaptor& x, const allocator_adaptor& y)noexcept { return x.allocator()==y.allocator(); } template bool operator!=( const allocator_adaptor& x, const allocator_adaptor& y)noexcept { return !(x==y); } } /* namespace poly_collection::detail */ } /* namespace poly_collection */ } /* namespace boost */ #endif