| 1 | /* Copyright 2018 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 | #ifndef BOOST_POLY_COLLECTION_DETAIL_ALLOCATOR_ADAPTOR_HPP |
| 10 | #define BOOST_POLY_COLLECTION_DETAIL_ALLOCATOR_ADAPTOR_HPP |
| 11 | |
| 12 | #if defined(_MSC_VER) |
| 13 | #pragma once |
| 14 | #endif |
| 15 | |
| 16 | #include <boost/mp11/function.hpp> |
| 17 | #include <boost/mp11/integer_sequence.hpp> |
| 18 | #include <boost/poly_collection/detail/is_constructible.hpp> |
| 19 | #include <new> |
| 20 | #include <memory> |
| 21 | #include <tuple> |
| 22 | #include <type_traits> |
| 23 | #include <utility> |
| 24 | |
| 25 | namespace boost{ |
| 26 | |
| 27 | namespace poly_collection{ |
| 28 | |
| 29 | namespace detail{ |
| 30 | |
| 31 | /* [container.requirements.general]/3 state that containers must use the |
| 32 | * allocator's construct/destroy member functions to construct/destroy |
| 33 | * elements and *not at all* for any other type. Since poly_collection is |
| 34 | * implemented as a multi-level structure of container and container-like |
| 35 | * objects, we need to use an adaptor for the user-provided Allocator that |
| 36 | * prevents intermediate entities from calling Allocator::[construct|destroy]. |
| 37 | * allocator_adaptor<Allocator> does this by taking advantage of the fact that |
| 38 | * elements are ultimately held within a value_holder: |
| 39 | * - construct(value_holder<T>*,...) uses placement new construction and |
| 40 | * passes the wrapped Allocator object for value_holder<T> to use for |
| 41 | * its inner construction of T. |
| 42 | * - For the rest of types, construct uses placement new construction and |
| 43 | * passes down the adaptor object itself as an argument following an |
| 44 | * approach analogous to that of std::scoped_allocator_adaptor. |
| 45 | * - destroy(value_holder<T>) resorts to Allocator::destroy to destroy the |
| 46 | * contained T element. |
| 47 | * - For the rest of types, destroy(T) calls ~T directly. |
| 48 | * |
| 49 | * Code has been ripped and adapted from libc++'s implementation of |
| 50 | * std::scoped_allocator_adaptor. |
| 51 | */ |
| 52 | |
| 53 | template<typename T> |
| 54 | class value_holder_base; |
| 55 | |
| 56 | template<typename T> |
| 57 | class value_holder; |
| 58 | |
| 59 | template<typename T,typename Allocator,typename... Args> |
| 60 | struct uses_alloc_ctor_impl |
| 61 | { |
| 62 | using RawAllocator=typename std::remove_cv< |
| 63 | typename std::remove_reference<Allocator>::type |
| 64 | >::type; |
| 65 | |
| 66 | static const bool ua=std::uses_allocator<T,RawAllocator>::value; |
| 67 | static const int ic=is_constructible< |
| 68 | T,std::allocator_arg_t,Allocator,Args...>::value?1:0; |
| 69 | static const int value=ua?2-ic:0; |
| 70 | }; |
| 71 | |
| 72 | template<typename T,typename Allocator,typename... Args> |
| 73 | struct uses_alloc_ctor: |
| 74 | std::integral_constant<int,uses_alloc_ctor_impl<T,Allocator,Args...>::value> |
| 75 | {}; |
| 76 | |
| 77 | template<typename Allocator,typename=void> |
| 78 | struct allocator_is_always_equal:std::is_empty<Allocator>{}; |
| 79 | |
| 80 | template<typename Allocator> |
| 81 | struct allocator_is_always_equal< |
| 82 | Allocator, |
| 83 | mp11::mp_void< |
| 84 | typename std::allocator_traits<Allocator>::is_always_equal |
| 85 | > |
| 86 | >:std::allocator_traits<Allocator>::is_always_equal{}; |
| 87 | |
| 88 | template<typename Allocator> |
| 89 | struct allocator_adaptor:Allocator |
| 90 | { |
| 91 | using traits=std::allocator_traits<Allocator>; |
| 92 | |
| 93 | using value_type=typename traits::value_type; |
| 94 | using size_type=typename traits::size_type; |
| 95 | using difference_type=typename traits::difference_type; |
| 96 | using pointer=typename traits::pointer; |
| 97 | using const_pointer=typename traits::const_pointer; |
| 98 | using void_pointer=typename traits::void_pointer; |
| 99 | using const_void_pointer=typename traits::const_void_pointer; |
| 100 | using propagate_on_container_copy_assignment= |
| 101 | typename traits::propagate_on_container_copy_assignment; |
| 102 | using propagate_on_container_move_assignment= |
| 103 | typename traits::propagate_on_container_move_assignment; |
| 104 | using propagate_on_container_swap= |
| 105 | typename traits::propagate_on_container_swap; |
| 106 | using is_always_equal=typename allocator_is_always_equal<Allocator>::type; |
| 107 | |
| 108 | template<typename U> |
| 109 | struct rebind |
| 110 | { |
| 111 | using other=allocator_adaptor<typename traits::template rebind_alloc<U>>; |
| 112 | }; |
| 113 | |
| 114 | allocator_adaptor()=default; |
| 115 | allocator_adaptor(const allocator_adaptor&)=default; |
| 116 | |
| 117 | template< |
| 118 | typename Allocator2, |
| 119 | typename std::enable_if< |
| 120 | is_constructible<Allocator,const Allocator2&>::value |
| 121 | >::type* =nullptr |
| 122 | > |
| 123 | allocator_adaptor(const Allocator2& x)noexcept:Allocator{x}{} |
| 124 | |
| 125 | template< |
| 126 | typename Allocator2, |
| 127 | typename std::enable_if< |
| 128 | is_constructible<Allocator,const Allocator2&>::value |
| 129 | >::type* =nullptr |
| 130 | > |
| 131 | allocator_adaptor(const allocator_adaptor<Allocator2>& x)noexcept: |
| 132 | Allocator{x.allocator()}{} |
| 133 | |
| 134 | allocator_adaptor& operator=(const allocator_adaptor&)=default; |
| 135 | |
| 136 | Allocator& allocator()noexcept{return *this;} |
| 137 | const Allocator& allocator()const noexcept{return *this;} |
| 138 | |
| 139 | template<typename T,typename... Args> |
| 140 | void construct(T* p,Args&&... args) |
| 141 | { |
| 142 | construct_( |
| 143 | uses_alloc_ctor<T,allocator_adaptor&,Args...>{}, |
| 144 | p,std::forward<Args>(args)...); |
| 145 | } |
| 146 | |
| 147 | template<typename T,typename... Args> |
| 148 | void construct(value_holder<T>* p,Args&&... args) |
| 149 | { |
| 150 | ::new ((void*)p) value_holder<T>(allocator(),std::forward<Args>(args)...); |
| 151 | } |
| 152 | |
| 153 | template<typename T1,typename T2,typename... Args1,typename... Args2> |
| 154 | void construct( |
| 155 | std::pair<T1,T2>* p,std::piecewise_construct_t, |
| 156 | std::tuple<Args1...> x,std::tuple<Args2...> y) |
| 157 | { |
| 158 | ::new ((void*)p) std::pair<T1,T2>( |
| 159 | std::piecewise_construct, |
| 160 | transform_tuple( |
| 161 | uses_alloc_ctor<T1,allocator_adaptor&,Args1...>{}, |
| 162 | std::move(x), |
| 163 | mp11::make_index_sequence<sizeof...(Args1)>{}), |
| 164 | transform_tuple( |
| 165 | uses_alloc_ctor<T2,allocator_adaptor&,Args2...>{}, |
| 166 | std::move(y), |
| 167 | mp11::make_index_sequence<sizeof...(Args2)>{}) |
| 168 | ); |
| 169 | } |
| 170 | |
| 171 | template<typename T1,typename T2> |
| 172 | void construct(std::pair<T1,T2>* p) |
| 173 | { |
| 174 | construct(p,std::piecewise_construct,std::tuple<>{},std::tuple<>{}); |
| 175 | } |
| 176 | |
| 177 | template<typename T1,typename T2,typename U,typename V> |
| 178 | void construct(std::pair<T1,T2>* p,U&& x,V&& y) |
| 179 | { |
| 180 | construct( |
| 181 | p,std::piecewise_construct, |
| 182 | std::forward_as_tuple(std::forward<U>(x)), |
| 183 | std::forward_as_tuple(std::forward<V>(y))); |
| 184 | } |
| 185 | |
| 186 | template<typename T1,typename T2,typename U,typename V> |
| 187 | void construct(std::pair<T1,T2>* p,const std::pair<U,V>& x) |
| 188 | { |
| 189 | construct( |
| 190 | p,std::piecewise_construct, |
| 191 | std::forward_as_tuple(x.first),std::forward_as_tuple(x.second)); |
| 192 | } |
| 193 | |
| 194 | template<typename T1,typename T2,typename U,typename V> |
| 195 | void construct(std::pair<T1,T2>* p,std::pair<U,V>&& x) |
| 196 | { |
| 197 | construct( |
| 198 | p,std::piecewise_construct, |
| 199 | std::forward_as_tuple(std::forward<U>(x.first)), |
| 200 | std::forward_as_tuple(std::forward<V>(x.second))); |
| 201 | } |
| 202 | |
| 203 | template<typename T> |
| 204 | void destroy(T* p) |
| 205 | { |
| 206 | p->~T(); |
| 207 | } |
| 208 | |
| 209 | template<typename T> |
| 210 | void destroy(value_holder<T>* p) |
| 211 | { |
| 212 | traits::destroy( |
| 213 | allocator(), |
| 214 | reinterpret_cast<T*>(static_cast<value_holder_base<T>*>(p))); |
| 215 | } |
| 216 | |
| 217 | allocator_adaptor |
| 218 | select_on_container_copy_construction()const noexcept |
| 219 | { |
| 220 | return traits::select_on_container_copy_construction(allocator()); |
| 221 | } |
| 222 | |
| 223 | private: |
| 224 | template<typename T,typename... Args> |
| 225 | void construct_( |
| 226 | std::integral_constant<int,0>, /* doesn't use allocator */ |
| 227 | T* p,Args&&... args) |
| 228 | { |
| 229 | ::new ((void*)p) T(std::forward<Args>(args)...); |
| 230 | } |
| 231 | |
| 232 | template<typename T,typename... Args> |
| 233 | void construct_( |
| 234 | std::integral_constant<int,1>, /* with std::allocator_arg */ |
| 235 | T* p,Args&&... args) |
| 236 | { |
| 237 | ::new ((void*)p) T(std::allocator_arg,*this,std::forward<Args>(args)...); |
| 238 | } |
| 239 | |
| 240 | template<typename T,typename... Args> |
| 241 | void construct_( |
| 242 | std::integral_constant<int,2>, /* allocator at the end */ |
| 243 | T* p,Args&&... args) |
| 244 | { |
| 245 | ::new ((void*)p) T(std::forward<Args>(args)...,*this); |
| 246 | } |
| 247 | |
| 248 | template<typename... Args,std::size_t... I> |
| 249 | std::tuple<Args&&...> transform_tuple( |
| 250 | std::integral_constant<int,0>, /* doesn't use allocator */ |
| 251 | std::tuple<Args...>&& t,mp11::index_sequence<I...>) |
| 252 | { |
| 253 | return std::tuple<Args&&...>(std::get<I>(std::move(t))...); |
| 254 | } |
| 255 | |
| 256 | template<typename... Args,std::size_t... I> |
| 257 | std::tuple<std::allocator_arg_t,allocator_adaptor&,Args&&...> |
| 258 | transform_tuple( |
| 259 | std::integral_constant<int,1>, /* with std::allocator_arg */ |
| 260 | std::tuple<Args...>&& t,mp11::index_sequence<I...>) |
| 261 | { |
| 262 | return std::tuple< |
| 263 | std::allocator_arg_t,allocator_adaptor&,Args&&...>( |
| 264 | std::allocator_arg,*this,std::get<I>(std::move(t))...); |
| 265 | } |
| 266 | |
| 267 | template<typename... Args,std::size_t... I> |
| 268 | std::tuple<Args&&...,allocator_adaptor&> |
| 269 | transform_tuple( |
| 270 | std::integral_constant<int,2>, /* allocator at the end */ |
| 271 | std::tuple<Args...>&& t,mp11::index_sequence<I...>) |
| 272 | { |
| 273 | return std::tuple<Args&&...,allocator_adaptor&>( |
| 274 | std::get<I>(std::move(t))...,*this); |
| 275 | } |
| 276 | }; |
| 277 | |
| 278 | template<typename Allocator1,typename Allocator2> |
| 279 | bool operator==( |
| 280 | const allocator_adaptor<Allocator1>& x, |
| 281 | const allocator_adaptor<Allocator2>& y)noexcept |
| 282 | { |
| 283 | return x.allocator()==y.allocator(); |
| 284 | } |
| 285 | |
| 286 | template<typename Allocator1,typename Allocator2> |
| 287 | bool operator!=( |
| 288 | const allocator_adaptor<Allocator1>& x, |
| 289 | const allocator_adaptor<Allocator2>& y)noexcept |
| 290 | { |
| 291 | return !(x==y); |
| 292 | } |
| 293 | |
| 294 | } /* namespace poly_collection::detail */ |
| 295 | |
| 296 | } /* namespace poly_collection */ |
| 297 | |
| 298 | } /* namespace boost */ |
| 299 | |
| 300 | #endif |
| 301 | |