317 lines
9.5 KiB
C++
317 lines
9.5 KiB
C++
|
/* 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_SEGMENT_HPP
|
||
|
#define BOOST_POLY_COLLECTION_DETAIL_SEGMENT_HPP
|
||
|
|
||
|
#if defined(_MSC_VER)
|
||
|
#pragma once
|
||
|
#endif
|
||
|
|
||
|
#include <iterator>
|
||
|
#include <memory>
|
||
|
#include <type_traits>
|
||
|
#include <utility>
|
||
|
|
||
|
namespace boost{
|
||
|
|
||
|
namespace poly_collection{
|
||
|
|
||
|
namespace detail{
|
||
|
|
||
|
/* segment<Model,Allocator> encapsulates implementations of
|
||
|
* Model::segment_backend virtual interface under a value-semantics type for
|
||
|
* use by poly_collection. The techique is described by Sean Parent at slides
|
||
|
* 157-205 of
|
||
|
* https://github.com/sean-parent/sean-parent.github.com/wiki/
|
||
|
* presentations/2013-09-11-cpp-seasoning/cpp-seasoning.pdf
|
||
|
* with one twist: when the type of the implementation can be known at compile
|
||
|
* time, a downcast is done and non-virtual member functions (named with a nv_
|
||
|
* prefix) are used: this increases the performance of some operations.
|
||
|
*/
|
||
|
|
||
|
template<typename Model,typename Allocator>
|
||
|
class segment
|
||
|
{
|
||
|
public:
|
||
|
using value_type=typename Model::value_type;
|
||
|
using allocator_type=Allocator; /* needed for uses-allocator construction */
|
||
|
using base_iterator=typename Model::base_iterator;
|
||
|
using const_base_iterator=typename Model::const_base_iterator;
|
||
|
using base_sentinel=typename Model::base_sentinel;
|
||
|
using const_base_sentinel=typename Model::const_base_sentinel;
|
||
|
template<typename T>
|
||
|
using iterator=typename Model::template iterator<T>;
|
||
|
template<typename T>
|
||
|
using const_iterator=typename Model::template const_iterator<T>;
|
||
|
|
||
|
template<typename T>
|
||
|
static segment make(const allocator_type& al)
|
||
|
{
|
||
|
return segment_backend_implementation<T>::make(al);
|
||
|
}
|
||
|
|
||
|
/* clones the implementation of x with no elements */
|
||
|
|
||
|
static segment make_from_prototype(const segment& x,const allocator_type& al)
|
||
|
{
|
||
|
return {from_prototype{},x,al};
|
||
|
}
|
||
|
|
||
|
segment(const segment& x):
|
||
|
pimpl{x.impl().copy()}{set_sentinel();}
|
||
|
segment(segment&& x)=default;
|
||
|
segment(const segment& x,const allocator_type& al):
|
||
|
pimpl{x.impl().copy(al)}{set_sentinel();}
|
||
|
|
||
|
/* TODO: try ptr-level move before impl().move() */
|
||
|
segment(segment&& x,const allocator_type& al):
|
||
|
pimpl{x.impl().move(al)}{set_sentinel();}
|
||
|
|
||
|
segment& operator=(const segment& x)
|
||
|
{
|
||
|
pimpl=allocator_traits::propagate_on_container_copy_assignment::value?
|
||
|
x.impl().copy():x.impl().copy(impl().get_allocator());
|
||
|
set_sentinel();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
segment& operator=(segment&& x)
|
||
|
{
|
||
|
pimpl=x.impl().move(
|
||
|
allocator_traits::propagate_on_container_move_assignment::value?
|
||
|
x.impl().get_allocator():impl().get_allocator());
|
||
|
set_sentinel();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
friend bool operator==(const segment& x,const segment& y)
|
||
|
{
|
||
|
if(typeid(*(x.pimpl))!=typeid(*(y.pimpl)))return false;
|
||
|
else return x.impl().equal(y.impl());
|
||
|
}
|
||
|
|
||
|
friend bool operator!=(const segment& x,const segment& y){return !(x==y);}
|
||
|
|
||
|
base_iterator begin()const noexcept{return impl().begin();}
|
||
|
template<typename U>
|
||
|
base_iterator begin()const noexcept{return impl<U>().nv_begin();}
|
||
|
base_iterator end()const noexcept{return impl().end();}
|
||
|
template<typename U>
|
||
|
base_iterator end()const noexcept{return impl<U>().nv_end();}
|
||
|
base_sentinel sentinel()const noexcept{return snt;}
|
||
|
bool empty()const noexcept{return impl().empty();}
|
||
|
template<typename U>
|
||
|
bool empty()const noexcept{return impl<U>().nv_empty();}
|
||
|
std::size_t size()const noexcept{return impl().size();}
|
||
|
template<typename U>
|
||
|
std::size_t size()const noexcept{return impl<U>().nv_size();}
|
||
|
std::size_t max_size()const noexcept{return impl().max_size();}
|
||
|
template<typename U>
|
||
|
std::size_t max_size()const noexcept
|
||
|
{return impl<U>().nv_max_size();}
|
||
|
void reserve(std::size_t n){filter(impl().reserve(n));}
|
||
|
template<typename U>
|
||
|
void reserve(std::size_t n){filter(impl<U>().nv_reserve(n));}
|
||
|
std::size_t capacity()const noexcept{return impl().capacity();}
|
||
|
template<typename U>
|
||
|
std::size_t capacity()const noexcept
|
||
|
{return impl<U>().nv_capacity();}
|
||
|
void shrink_to_fit(){filter(impl().shrink_to_fit());}
|
||
|
template<typename U>
|
||
|
void shrink_to_fit(){filter(impl<U>().nv_shrink_to_fit());}
|
||
|
|
||
|
template<typename U,typename Iterator,typename... Args>
|
||
|
base_iterator emplace(Iterator it,Args&&... args)
|
||
|
{
|
||
|
return filter(impl<U>().nv_emplace(it,std::forward<Args>(args)...));
|
||
|
}
|
||
|
|
||
|
template<typename U,typename... Args>
|
||
|
base_iterator emplace_back(Args&&... args)
|
||
|
{
|
||
|
return filter(impl<U>().nv_emplace_back(std::forward<Args>(args)...));
|
||
|
}
|
||
|
|
||
|
template<typename T>
|
||
|
base_iterator push_back(const T& x)
|
||
|
{
|
||
|
return filter(impl().push_back(subaddress(x)));
|
||
|
}
|
||
|
|
||
|
template<
|
||
|
typename T,
|
||
|
typename std::enable_if<
|
||
|
!std::is_lvalue_reference<T>::value&&!std::is_const<T>::value
|
||
|
>::type* =nullptr
|
||
|
>
|
||
|
base_iterator push_back(T&& x)
|
||
|
{
|
||
|
return filter(impl().push_back_move(subaddress(x)));
|
||
|
}
|
||
|
|
||
|
template<typename U>
|
||
|
base_iterator push_back_terminal(U&& x)
|
||
|
{
|
||
|
return filter(
|
||
|
impl<typename std::decay<U>::type>().nv_push_back(std::forward<U>(x)));
|
||
|
}
|
||
|
|
||
|
template<typename T>
|
||
|
base_iterator insert(const_base_iterator it,const T& x)
|
||
|
{
|
||
|
return filter(impl().insert(it,subaddress(x)));
|
||
|
}
|
||
|
|
||
|
template<typename U,typename T>
|
||
|
base_iterator insert(const_iterator<U> it,const T& x)
|
||
|
{
|
||
|
return filter(
|
||
|
impl<U>().nv_insert(it,*static_cast<const U*>(subaddress(x))));
|
||
|
}
|
||
|
|
||
|
template<
|
||
|
typename T,
|
||
|
typename std::enable_if<
|
||
|
!std::is_lvalue_reference<T>::value&&!std::is_const<T>::value
|
||
|
>::type* =nullptr
|
||
|
>
|
||
|
base_iterator insert(const_base_iterator it,T&& x)
|
||
|
{
|
||
|
return filter(impl().insert_move(it,subaddress(x)));
|
||
|
}
|
||
|
|
||
|
template<
|
||
|
typename U,typename T,
|
||
|
typename std::enable_if<
|
||
|
!std::is_lvalue_reference<T>::value&&!std::is_const<T>::value
|
||
|
>::type* =nullptr
|
||
|
>
|
||
|
base_iterator insert(const_iterator<U> it,T&& x)
|
||
|
{
|
||
|
return filter(
|
||
|
impl<U>().nv_insert(it,std::move(*static_cast<U*>(subaddress(x)))));
|
||
|
}
|
||
|
|
||
|
template<typename InputIterator>
|
||
|
base_iterator insert(InputIterator first,InputIterator last)
|
||
|
{
|
||
|
return filter(
|
||
|
impl<typename std::iterator_traits<InputIterator>::value_type>().
|
||
|
nv_insert(first,last));
|
||
|
}
|
||
|
|
||
|
template<typename InputIterator>
|
||
|
base_iterator insert(
|
||
|
const_base_iterator it,InputIterator first,InputIterator last)
|
||
|
{
|
||
|
return insert(
|
||
|
const_iterator<
|
||
|
typename std::iterator_traits<InputIterator>::value_type>(it),
|
||
|
first,last);
|
||
|
}
|
||
|
|
||
|
template<typename U,typename InputIterator>
|
||
|
base_iterator insert(
|
||
|
const_iterator<U> it,InputIterator first,InputIterator last)
|
||
|
{
|
||
|
return filter(impl<U>().nv_insert(it,first,last));
|
||
|
}
|
||
|
|
||
|
base_iterator erase(const_base_iterator it)
|
||
|
{
|
||
|
return filter(impl().erase(it));
|
||
|
}
|
||
|
|
||
|
template<typename U>
|
||
|
base_iterator erase(const_iterator<U> it)
|
||
|
{
|
||
|
return filter(impl<U>().nv_erase(it));
|
||
|
}
|
||
|
|
||
|
base_iterator erase(const_base_iterator f,const_base_iterator l)
|
||
|
{
|
||
|
return filter(impl().erase(f,l));
|
||
|
}
|
||
|
|
||
|
template<typename U>
|
||
|
base_iterator erase(const_iterator<U> f,const_iterator<U> l)
|
||
|
{
|
||
|
return filter(impl<U>().nv_erase(f,l));
|
||
|
}
|
||
|
|
||
|
template<typename Iterator>
|
||
|
base_iterator erase_till_end(Iterator f)
|
||
|
{
|
||
|
return filter(impl().erase_till_end(f));
|
||
|
}
|
||
|
|
||
|
template<typename Iterator>
|
||
|
base_iterator erase_from_begin(Iterator l)
|
||
|
{
|
||
|
return filter(impl().erase_from_begin(l));
|
||
|
}
|
||
|
|
||
|
void clear()noexcept{filter(impl().clear());}
|
||
|
template<typename U>
|
||
|
void clear()noexcept{filter(impl<U>().nv_clear());}
|
||
|
|
||
|
private:
|
||
|
using allocator_traits=std::allocator_traits<Allocator>;
|
||
|
using segment_backend=typename Model::template segment_backend<Allocator>;
|
||
|
template<typename Concrete>
|
||
|
using segment_backend_implementation=typename Model::
|
||
|
template segment_backend_implementation<Concrete,Allocator>;
|
||
|
using segment_backend_unique_ptr=
|
||
|
typename segment_backend::segment_backend_unique_ptr;
|
||
|
using range=typename segment_backend::range;
|
||
|
|
||
|
struct from_prototype{};
|
||
|
|
||
|
segment(segment_backend_unique_ptr&& pimpl):
|
||
|
pimpl{std::move(pimpl)}{set_sentinel();}
|
||
|
segment(from_prototype,const segment& x,const allocator_type& al):
|
||
|
pimpl{x.impl().empty_copy(al)}{set_sentinel();}
|
||
|
|
||
|
segment_backend& impl()noexcept{return *pimpl;}
|
||
|
const segment_backend& impl()const noexcept{return *pimpl;}
|
||
|
|
||
|
template<typename Concrete>
|
||
|
segment_backend_implementation<Concrete>& impl()noexcept
|
||
|
{
|
||
|
return static_cast<segment_backend_implementation<Concrete>&>(impl());
|
||
|
}
|
||
|
|
||
|
template<typename Concrete>
|
||
|
const segment_backend_implementation<Concrete>& impl()const noexcept
|
||
|
{
|
||
|
return
|
||
|
static_cast<const segment_backend_implementation<Concrete>&>(impl());
|
||
|
}
|
||
|
|
||
|
template<typename T>
|
||
|
static void* subaddress(T& x){return Model::subaddress(x);}
|
||
|
template<typename T>
|
||
|
static const void* subaddress(const T& x){return Model::subaddress(x);}
|
||
|
|
||
|
void set_sentinel(){filter(impl().end());}
|
||
|
void filter(base_sentinel x){snt=x;}
|
||
|
base_iterator filter(const range& x){snt=x.second;return x.first;}
|
||
|
|
||
|
segment_backend_unique_ptr pimpl;
|
||
|
base_sentinel snt;
|
||
|
};
|
||
|
|
||
|
} /* namespace poly_collection::detail */
|
||
|
|
||
|
} /* namespace poly_collection */
|
||
|
|
||
|
} /* namespace boost */
|
||
|
|
||
|
#endif
|