/* Copyright 2016-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_PACKED_SEGMENT_HPP #define BOOST_POLY_COLLECTION_DETAIL_PACKED_SEGMENT_HPP #if defined(_MSC_VER) #pragma once #endif #include #include #include #include #include #include #include namespace boost{ namespace poly_collection{ namespace detail{ /* segment_backend implementation where value_type& and Concrete& actually refer * to the same stored entity. * * Requires: * - [const_]base_iterator is a stride iterator constructible from * {value_type*,sizeof(store_value_type)}. * - Model provides a function value_ptr for * const Concrete* -> const value_type* conversion. */ template class packed_segment:public segment_backend { using value_type=typename Model::value_type; using store_value_type=value_holder; using store=std::vector< store_value_type, typename std::allocator_traits:: template rebind_alloc >; using store_iterator=typename store::iterator; using const_store_iterator=typename store::const_iterator; using segment_backend=detail::segment_backend; using typename segment_backend::segment_backend_unique_ptr; using typename segment_backend::value_pointer; using typename segment_backend::const_value_pointer; using typename segment_backend::base_iterator; using typename segment_backend::const_base_iterator; using const_iterator= typename segment_backend::template const_iterator; using typename segment_backend::base_sentinel; using typename segment_backend::range; using segment_allocator_type=typename std::allocator_traits:: template rebind_alloc; public: virtual ~packed_segment()=default; static segment_backend_unique_ptr make(const segment_allocator_type& al) { return new_(al,al); } virtual segment_backend_unique_ptr copy()const { return new_(s.get_allocator(),store{s}); } virtual segment_backend_unique_ptr copy(const Allocator& al)const { return new_(al,store{s,al}); } virtual segment_backend_unique_ptr empty_copy(const Allocator& al)const { return new_(al,al); } virtual segment_backend_unique_ptr move(const Allocator& al)const { return new_(al,store{std::move(s),al}); } virtual bool equal(const segment_backend& x)const { return s==static_cast(x).s; } virtual Allocator get_allocator()const noexcept{return s.get_allocator();} virtual base_iterator begin()const noexcept{return nv_begin();} base_iterator nv_begin()const noexcept { return {value_ptr(s.data()),sizeof(store_value_type)}; } virtual base_iterator end()const noexcept{return nv_end();} base_iterator nv_end()const noexcept { return {value_ptr(s.data()+s.size()),sizeof(store_value_type)}; } virtual bool empty()const noexcept{return nv_empty();} bool nv_empty()const noexcept{return s.empty();} virtual std::size_t size()const noexcept{return nv_size();} std::size_t nv_size()const noexcept{return s.size();} virtual std::size_t max_size()const noexcept{return nv_max_size();} std::size_t nv_max_size()const noexcept{return s.max_size();} virtual std::size_t capacity()const noexcept{return nv_capacity();} std::size_t nv_capacity()const noexcept{return s.capacity();} virtual base_sentinel reserve(std::size_t n){return nv_reserve(n);} base_sentinel nv_reserve(std::size_t n) {s.reserve(n);return sentinel();} virtual base_sentinel shrink_to_fit(){return nv_shrink_to_fit();} base_sentinel nv_shrink_to_fit() {s.shrink_to_fit();return sentinel();} template range nv_emplace(Iterator p,Args&&... args) { return range_from( s.emplace( iterator_from(p), value_holder_emplacing_ctor,std::forward(args)...)); } template range nv_emplace_back(Args&&... args) { s.emplace_back(value_holder_emplacing_ctor,std::forward(args)...); return range_from(s.size()-1); } virtual range push_back(const_value_pointer x) {return nv_push_back(const_concrete_ref(x));} range nv_push_back(const Concrete& x) { s.emplace_back(x); return range_from(s.size()-1); } virtual range push_back_move(value_pointer x) {return nv_push_back(std::move(concrete_ref(x)));} range nv_push_back(Concrete&& x) { s.emplace_back(std::move(x)); return range_from(s.size()-1); } virtual range insert(const_base_iterator p,const_value_pointer x) {return nv_insert(const_iterator(p),const_concrete_ref(x));} range nv_insert(const_iterator p,const Concrete& x) { return range_from(s.emplace(iterator_from(p),x)); } virtual range insert_move(const_base_iterator p,value_pointer x) {return nv_insert(const_iterator(p),std::move(concrete_ref(x)));} range nv_insert(const_iterator p,Concrete&& x) { return range_from(s.emplace(iterator_from(p),std::move(x))); } template range nv_insert(InputIterator first,InputIterator last) { return nv_insert( const_iterator(concrete_ptr(s.data()+s.size())),first,last); } template range nv_insert(const_iterator p,InputIterator first,InputIterator last) { #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900) /* std::vector::insert(pos,first,last) returns void rather than iterator */ auto n=const_store_value_type_ptr(p)-s.data(); s.insert(s.begin()+n,first,last); return range_from(static_cast(n)); #else return range_from(s.insert(iterator_from(p),first,last)); #endif } virtual range erase(const_base_iterator p) {return nv_erase(const_iterator(p));} range nv_erase(const_iterator p) { return range_from(s.erase(iterator_from(p))); } virtual range erase(const_base_iterator first,const_base_iterator last) {return nv_erase(const_iterator(first),const_iterator(last));} range nv_erase(const_iterator first,const_iterator last) { return range_from(s.erase(iterator_from(first),iterator_from(last))); } virtual range erase_till_end(const_base_iterator first) { return range_from(s.erase(iterator_from(first),s.end())); } virtual range erase_from_begin(const_base_iterator last) { return range_from(s.erase(s.begin(),iterator_from(last))); } virtual base_sentinel clear()noexcept{return nv_clear();} base_sentinel nv_clear()noexcept{s.clear();return sentinel();} private: template static segment_backend_unique_ptr new_( segment_allocator_type al,Args&&... args) { auto p=std::allocator_traits::allocate(al,1); try{ ::new ((void*)p) packed_segment{std::forward(args)...}; } catch(...){ std::allocator_traits::deallocate(al,p,1); throw; } return {p,&delete_}; } static void delete_(segment_backend* p) { auto q=static_cast(p); auto al=segment_allocator_type{q->s.get_allocator()}; q->~packed_segment(); std::allocator_traits::deallocate(al,q,1); } packed_segment(const Allocator& al):s{typename store::allocator_type{al}}{} packed_segment(store&& s):s{std::move(s)}{} static Concrete& concrete_ref(value_pointer p)noexcept { return *static_cast(p); } static const Concrete& const_concrete_ref(const_value_pointer p)noexcept { return *static_cast(p); } static Concrete* concrete_ptr(store_value_type* p)noexcept { return reinterpret_cast( static_cast*>(p)); } static const Concrete* const_concrete_ptr(const store_value_type* p)noexcept { return concrete_ptr(const_cast(p)); } static value_type* value_ptr(const store_value_type* p)noexcept { return const_cast(Model::value_ptr(const_concrete_ptr(p))); } static const store_value_type* const_store_value_type_ptr( const Concrete* p)noexcept { return static_cast*>( reinterpret_cast*>(p)); } /* It would have sufficed if iterator_from returned const_store_iterator * except for the fact that some old versions of libstdc++ claiming to be * C++11 compliant do not however provide std::vector modifier ops taking * const_iterator's. */ store_iterator iterator_from(const_base_iterator p) { return iterator_from(static_cast(p)); } store_iterator iterator_from(const_iterator p) { return s.begin()+(const_store_value_type_ptr(p)-s.data()); } base_sentinel sentinel()const noexcept { return base_iterator{ value_ptr(s.data()+s.size()), sizeof(store_value_type) }; } range range_from(const_store_iterator it)const { return { {value_ptr(s.data()+(it-s.begin())),sizeof(store_value_type)}, sentinel() }; } range range_from(std::size_t n)const { return { {value_ptr(s.data()+n),sizeof(store_value_type)}, sentinel() }; } store s; }; } /* namespace poly_collection::detail */ } /* namespace poly_collection */ } /* namespace boost */ #endif