| 1 | /* Copyright 2016-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_SEGMENT_HPP |
| 10 | #define BOOST_POLY_COLLECTION_DETAIL_SEGMENT_HPP |
| 11 | |
| 12 | #if defined(_MSC_VER) |
| 13 | #pragma once |
| 14 | #endif |
| 15 | |
| 16 | #include <iterator> |
| 17 | #include <memory> |
| 18 | #include <type_traits> |
| 19 | #include <utility> |
| 20 | |
| 21 | namespace boost{ |
| 22 | |
| 23 | namespace poly_collection{ |
| 24 | |
| 25 | namespace detail{ |
| 26 | |
| 27 | /* segment<Model,Allocator> encapsulates implementations of |
| 28 | * Model::segment_backend virtual interface under a value-semantics type for |
| 29 | * use by poly_collection. The techique is described by Sean Parent at slides |
| 30 | * 157-205 of |
| 31 | * https://github.com/sean-parent/sean-parent.github.com/wiki/ |
| 32 | * presentations/2013-09-11-cpp-seasoning/cpp-seasoning.pdf |
| 33 | * with one twist: when the type of the implementation can be known at compile |
| 34 | * time, a downcast is done and non-virtual member functions (named with a nv_ |
| 35 | * prefix) are used: this increases the performance of some operations. |
| 36 | */ |
| 37 | |
| 38 | template<typename Model,typename Allocator> |
| 39 | class segment |
| 40 | { |
| 41 | public: |
| 42 | using value_type=typename Model::value_type; |
| 43 | using allocator_type=Allocator; /* needed for uses-allocator construction */ |
| 44 | using base_iterator=typename Model::base_iterator; |
| 45 | using const_base_iterator=typename Model::const_base_iterator; |
| 46 | using base_sentinel=typename Model::base_sentinel; |
| 47 | using const_base_sentinel=typename Model::const_base_sentinel; |
| 48 | template<typename T> |
| 49 | using iterator=typename Model::template iterator<T>; |
| 50 | template<typename T> |
| 51 | using const_iterator=typename Model::template const_iterator<T>; |
| 52 | |
| 53 | template<typename T> |
| 54 | static segment make(const allocator_type& al) |
| 55 | { |
| 56 | return segment_backend_implementation<T>::make(al); |
| 57 | } |
| 58 | |
| 59 | /* clones the implementation of x with no elements */ |
| 60 | |
| 61 | static segment make_from_prototype(const segment& x,const allocator_type& al) |
| 62 | { |
| 63 | return {from_prototype{},x,al}; |
| 64 | } |
| 65 | |
| 66 | segment(const segment& x): |
| 67 | pimpl{x.impl().copy()}{set_sentinel();} |
| 68 | segment(segment&& x)=default; |
| 69 | segment(const segment& x,const allocator_type& al): |
| 70 | pimpl{x.impl().copy(al)}{set_sentinel();} |
| 71 | |
| 72 | /* TODO: try ptr-level move before impl().move() */ |
| 73 | segment(segment&& x,const allocator_type& al): |
| 74 | pimpl{x.impl().move(al)}{set_sentinel();} |
| 75 | |
| 76 | segment& operator=(const segment& x) |
| 77 | { |
| 78 | pimpl=allocator_traits::propagate_on_container_copy_assignment::value? |
| 79 | x.impl().copy():x.impl().copy(impl().get_allocator()); |
| 80 | set_sentinel(); |
| 81 | return *this; |
| 82 | } |
| 83 | |
| 84 | segment& operator=(segment&& x) |
| 85 | { |
| 86 | pimpl=x.impl().move( |
| 87 | allocator_traits::propagate_on_container_move_assignment::value? |
| 88 | x.impl().get_allocator():impl().get_allocator()); |
| 89 | set_sentinel(); |
| 90 | return *this; |
| 91 | } |
| 92 | |
| 93 | friend bool operator==(const segment& x,const segment& y) |
| 94 | { |
| 95 | if(typeid(*(x.pimpl))!=typeid(*(y.pimpl)))return false; |
| 96 | else return x.impl().equal(y.impl()); |
| 97 | } |
| 98 | |
| 99 | friend bool operator!=(const segment& x,const segment& y){return !(x==y);} |
| 100 | |
| 101 | base_iterator begin()const noexcept{return impl().begin();} |
| 102 | template<typename U> |
| 103 | base_iterator begin()const noexcept{return impl<U>().nv_begin();} |
| 104 | base_iterator end()const noexcept{return impl().end();} |
| 105 | template<typename U> |
| 106 | base_iterator end()const noexcept{return impl<U>().nv_end();} |
| 107 | base_sentinel sentinel()const noexcept{return snt;} |
| 108 | bool empty()const noexcept{return impl().empty();} |
| 109 | template<typename U> |
| 110 | bool empty()const noexcept{return impl<U>().nv_empty();} |
| 111 | std::size_t size()const noexcept{return impl().size();} |
| 112 | template<typename U> |
| 113 | std::size_t size()const noexcept{return impl<U>().nv_size();} |
| 114 | std::size_t max_size()const noexcept{return impl().max_size();} |
| 115 | template<typename U> |
| 116 | std::size_t max_size()const noexcept |
| 117 | {return impl<U>().nv_max_size();} |
| 118 | void reserve(std::size_t n){filter(impl().reserve(n));} |
| 119 | template<typename U> |
| 120 | void reserve(std::size_t n){filter(impl<U>().nv_reserve(n));} |
| 121 | std::size_t capacity()const noexcept{return impl().capacity();} |
| 122 | template<typename U> |
| 123 | std::size_t capacity()const noexcept |
| 124 | {return impl<U>().nv_capacity();} |
| 125 | void shrink_to_fit(){filter(impl().shrink_to_fit());} |
| 126 | template<typename U> |
| 127 | void shrink_to_fit(){filter(impl<U>().nv_shrink_to_fit());} |
| 128 | |
| 129 | template<typename U,typename Iterator,typename... Args> |
| 130 | base_iterator emplace(Iterator it,Args&&... args) |
| 131 | { |
| 132 | return filter(impl<U>().nv_emplace(it,std::forward<Args>(args)...)); |
| 133 | } |
| 134 | |
| 135 | template<typename U,typename... Args> |
| 136 | base_iterator emplace_back(Args&&... args) |
| 137 | { |
| 138 | return filter(impl<U>().nv_emplace_back(std::forward<Args>(args)...)); |
| 139 | } |
| 140 | |
| 141 | template<typename T> |
| 142 | base_iterator push_back(const T& x) |
| 143 | { |
| 144 | return filter(impl().push_back(subaddress(x))); |
| 145 | } |
| 146 | |
| 147 | template< |
| 148 | typename T, |
| 149 | typename std::enable_if< |
| 150 | !std::is_lvalue_reference<T>::value&&!std::is_const<T>::value |
| 151 | >::type* =nullptr |
| 152 | > |
| 153 | base_iterator push_back(T&& x) |
| 154 | { |
| 155 | return filter(impl().push_back_move(subaddress(x))); |
| 156 | } |
| 157 | |
| 158 | template<typename U> |
| 159 | base_iterator push_back_terminal(U&& x) |
| 160 | { |
| 161 | return filter( |
| 162 | impl<typename std::decay<U>::type>().nv_push_back(std::forward<U>(x))); |
| 163 | } |
| 164 | |
| 165 | template<typename T> |
| 166 | base_iterator insert(const_base_iterator it,const T& x) |
| 167 | { |
| 168 | return filter(impl().insert(it,subaddress(x))); |
| 169 | } |
| 170 | |
| 171 | template<typename U,typename T> |
| 172 | base_iterator insert(const_iterator<U> it,const T& x) |
| 173 | { |
| 174 | return filter( |
| 175 | impl<U>().nv_insert(it,*static_cast<const U*>(subaddress(x)))); |
| 176 | } |
| 177 | |
| 178 | template< |
| 179 | typename T, |
| 180 | typename std::enable_if< |
| 181 | !std::is_lvalue_reference<T>::value&&!std::is_const<T>::value |
| 182 | >::type* =nullptr |
| 183 | > |
| 184 | base_iterator insert(const_base_iterator it,T&& x) |
| 185 | { |
| 186 | return filter(impl().insert_move(it,subaddress(x))); |
| 187 | } |
| 188 | |
| 189 | template< |
| 190 | typename U,typename T, |
| 191 | typename std::enable_if< |
| 192 | !std::is_lvalue_reference<T>::value&&!std::is_const<T>::value |
| 193 | >::type* =nullptr |
| 194 | > |
| 195 | base_iterator insert(const_iterator<U> it,T&& x) |
| 196 | { |
| 197 | return filter( |
| 198 | impl<U>().nv_insert(it,std::move(*static_cast<U*>(subaddress(x))))); |
| 199 | } |
| 200 | |
| 201 | template<typename InputIterator> |
| 202 | base_iterator insert(InputIterator first,InputIterator last) |
| 203 | { |
| 204 | return filter( |
| 205 | impl<typename std::iterator_traits<InputIterator>::value_type>(). |
| 206 | nv_insert(first,last)); |
| 207 | } |
| 208 | |
| 209 | template<typename InputIterator> |
| 210 | base_iterator insert( |
| 211 | const_base_iterator it,InputIterator first,InputIterator last) |
| 212 | { |
| 213 | return insert( |
| 214 | const_iterator< |
| 215 | typename std::iterator_traits<InputIterator>::value_type>(it), |
| 216 | first,last); |
| 217 | } |
| 218 | |
| 219 | template<typename U,typename InputIterator> |
| 220 | base_iterator insert( |
| 221 | const_iterator<U> it,InputIterator first,InputIterator last) |
| 222 | { |
| 223 | return filter(impl<U>().nv_insert(it,first,last)); |
| 224 | } |
| 225 | |
| 226 | base_iterator erase(const_base_iterator it) |
| 227 | { |
| 228 | return filter(impl().erase(it)); |
| 229 | } |
| 230 | |
| 231 | template<typename U> |
| 232 | base_iterator erase(const_iterator<U> it) |
| 233 | { |
| 234 | return filter(impl<U>().nv_erase(it)); |
| 235 | } |
| 236 | |
| 237 | base_iterator erase(const_base_iterator f,const_base_iterator l) |
| 238 | { |
| 239 | return filter(impl().erase(f,l)); |
| 240 | } |
| 241 | |
| 242 | template<typename U> |
| 243 | base_iterator erase(const_iterator<U> f,const_iterator<U> l) |
| 244 | { |
| 245 | return filter(impl<U>().nv_erase(f,l)); |
| 246 | } |
| 247 | |
| 248 | template<typename Iterator> |
| 249 | base_iterator erase_till_end(Iterator f) |
| 250 | { |
| 251 | return filter(impl().erase_till_end(f)); |
| 252 | } |
| 253 | |
| 254 | template<typename Iterator> |
| 255 | base_iterator erase_from_begin(Iterator l) |
| 256 | { |
| 257 | return filter(impl().erase_from_begin(l)); |
| 258 | } |
| 259 | |
| 260 | void clear()noexcept{filter(impl().clear());} |
| 261 | template<typename U> |
| 262 | void clear()noexcept{filter(impl<U>().nv_clear());} |
| 263 | |
| 264 | private: |
| 265 | using allocator_traits=std::allocator_traits<Allocator>; |
| 266 | using segment_backend=typename Model::template segment_backend<Allocator>; |
| 267 | template<typename Concrete> |
| 268 | using segment_backend_implementation=typename Model:: |
| 269 | template segment_backend_implementation<Concrete,Allocator>; |
| 270 | using segment_backend_unique_ptr= |
| 271 | typename segment_backend::segment_backend_unique_ptr; |
| 272 | using range=typename segment_backend::range; |
| 273 | |
| 274 | struct from_prototype{}; |
| 275 | |
| 276 | segment(segment_backend_unique_ptr&& pimpl): |
| 277 | pimpl{std::move(pimpl)}{set_sentinel();} |
| 278 | segment(from_prototype,const segment& x,const allocator_type& al): |
| 279 | pimpl{x.impl().empty_copy(al)}{set_sentinel();} |
| 280 | |
| 281 | segment_backend& impl()noexcept{return *pimpl;} |
| 282 | const segment_backend& impl()const noexcept{return *pimpl;} |
| 283 | |
| 284 | template<typename Concrete> |
| 285 | segment_backend_implementation<Concrete>& impl()noexcept |
| 286 | { |
| 287 | return static_cast<segment_backend_implementation<Concrete>&>(impl()); |
| 288 | } |
| 289 | |
| 290 | template<typename Concrete> |
| 291 | const segment_backend_implementation<Concrete>& impl()const noexcept |
| 292 | { |
| 293 | return |
| 294 | static_cast<const segment_backend_implementation<Concrete>&>(impl()); |
| 295 | } |
| 296 | |
| 297 | template<typename T> |
| 298 | static void* subaddress(T& x){return Model::subaddress(x);} |
| 299 | template<typename T> |
| 300 | static const void* subaddress(const T& x){return Model::subaddress(x);} |
| 301 | |
| 302 | void set_sentinel(){filter(impl().end());} |
| 303 | void filter(base_sentinel x){snt=x;} |
| 304 | base_iterator filter(const range& x){snt=x.second;return x.first;} |
| 305 | |
| 306 | segment_backend_unique_ptr pimpl; |
| 307 | base_sentinel snt; |
| 308 | }; |
| 309 | |
| 310 | } /* namespace poly_collection::detail */ |
| 311 | |
| 312 | } /* namespace poly_collection */ |
| 313 | |
| 314 | } /* namespace boost */ |
| 315 | |
| 316 | #endif |
| 317 | |