diff options
Diffstat (limited to 'boost/poly_collection/detail/value_holder.hpp')
-rw-r--r-- | boost/poly_collection/detail/value_holder.hpp | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/boost/poly_collection/detail/value_holder.hpp b/boost/poly_collection/detail/value_holder.hpp new file mode 100644 index 0000000000..bd2f25c6f8 --- /dev/null +++ b/boost/poly_collection/detail/value_holder.hpp @@ -0,0 +1,313 @@ +/* Copyright 2016-2017 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_VALUE_HOLDER_HPP +#define BOOST_POLY_COLLECTION_DETAIL_VALUE_HOLDER_HPP + +#if defined(_MSC_VER) +#pragma once +#endif + +#include <boost/core/addressof.hpp> +#include <boost/poly_collection/detail/is_constructible.hpp> +#include <boost/poly_collection/detail/is_equality_comparable.hpp> +#include <boost/poly_collection/detail/is_nothrow_eq_comparable.hpp> +#include <boost/poly_collection/exception.hpp> +#include <new> +#include <memory> +#include <type_traits> +#include <utility> + +namespace boost{ + +namespace poly_collection{ + +namespace detail{ + +/* Segments of a poly_collection maintain vectors of value_holder<T> + * rather than directly T. This serves several purposes: + * - value_holder<T> is copy constructible and equality comparable even if T + * is not: executing the corresponding op results in a reporting exception + * being thrown. This allows the segment to offer its full virtual + * interface regardless of the properties of the concrete class contained. + * - value_holder<T> emulates move assignment when T is not move assignable + * (nothrow move constructibility required); this happens most notably with + * lambda functions, whose assignment operator is deleted by standard + * mandate [expr.prim.lambda]/20 even if the compiler generated one would + * work (capture by value). + * - To comply with [container.requirements.general]/3 (or, more precisely, + * its natural extension to polymorphic containers), value_holder ctors + * accept a first allocator arg passed by value_holder_allocator_adaptor, + * which must therefore be used in the vectors of value_holder's. + * + * A pointer to value_holder_base<T> can be reinterpret_cast'ed to T*. + * Emplacing is explicitly signalled with value_holder_emplacing_ctor to + * protect us from greedy T's constructible from anything (like + * boost::type_erasure::any). + */ + +struct value_holder_emplacing_ctor_t{}; +constexpr value_holder_emplacing_ctor_t value_holder_emplacing_ctor= + value_holder_emplacing_ctor_t(); + +template<typename T> +class value_holder_base +{ +protected: + typename std::aligned_storage<sizeof(T),alignof(T)>::type s; +}; + +template<typename T> +class value_holder:public value_holder_base<T> +{ + template<typename U> + using enable_if_not_emplacing_ctor_t=typename std::enable_if< + !std::is_same< + typename std::decay<U>::type,value_holder_emplacing_ctor_t + >::value + >::type*; + + using is_nothrow_move_constructible=std::is_nothrow_move_constructible<T>; + using is_copy_constructible=std::is_copy_constructible<T>; + using is_nothrow_copy_constructible=std::is_nothrow_copy_constructible<T>; + using is_move_assignable=std::is_move_assignable<T>; + using is_nothrow_move_assignable=std::is_nothrow_move_assignable<T>; + using is_equality_comparable=detail::is_equality_comparable<T>; + using is_nothrow_equality_comparable= + detail::is_nothrow_equality_comparable<T>; + + T* data()noexcept{return reinterpret_cast<T*>(&this->s);} + const T* data()const noexcept + {return reinterpret_cast<const T*>(&this->s);} + + T& value()noexcept{return *static_cast<T*>(data());} + const T& value()const noexcept{return *static_cast<const T*>(data());} + +public: + template< + typename Allocator, + enable_if_not_emplacing_ctor_t<Allocator> =nullptr + > + value_holder(Allocator& al,const T& x) + noexcept(is_nothrow_copy_constructible::value) + {copy(al,x);} + template< + typename Allocator, + enable_if_not_emplacing_ctor_t<Allocator> =nullptr + > + value_holder(Allocator& al,T&& x) + noexcept(is_nothrow_move_constructible::value) + {std::allocator_traits<Allocator>::construct(al,data(),std::move(x));} + template< + typename Allocator,typename... Args, + enable_if_not_emplacing_ctor_t<Allocator> =nullptr + > + value_holder(Allocator& al,value_holder_emplacing_ctor_t,Args&&... args) + {std::allocator_traits<Allocator>::construct( + al,data(),std::forward<Args>(args)...);} + template< + typename Allocator, + enable_if_not_emplacing_ctor_t<Allocator> =nullptr + > + value_holder(Allocator& al,const value_holder& x) + noexcept(is_nothrow_copy_constructible::value) + {copy(al,x.value());} + template< + typename Allocator, + enable_if_not_emplacing_ctor_t<Allocator> =nullptr + > + value_holder(Allocator& al,value_holder&& x) + noexcept(is_nothrow_move_constructible::value) + {std::allocator_traits<Allocator>::construct( + al,data(),std::move(x.value()));} + + /* stdlib implementations in current use are notoriously lacking at + * complying with [container.requirements.general]/3, so we keep the + * following to make their life easier. + */ + + value_holder(const T& x) + noexcept(is_nothrow_copy_constructible::value) + {copy(x);} + value_holder(T&& x) + noexcept(is_nothrow_move_constructible::value) + {::new ((void*)data()) T(std::move(x));} + template<typename... Args> + value_holder(value_holder_emplacing_ctor_t,Args&&... args) + {::new ((void*)data()) T(std::forward<Args>(args)...);} + value_holder(const value_holder& x) + noexcept(is_nothrow_copy_constructible::value) + {copy(x.value());} + value_holder(value_holder&& x) + noexcept(is_nothrow_move_constructible::value) + {::new ((void*)data()) T(std::move(x.value()));} + + value_holder& operator=(const value_holder& x)=delete; + value_holder& operator=(value_holder&& x) + noexcept(is_nothrow_move_assignable::value||!is_move_assignable::value) + /* if 2nd clause: nothrow move constructibility required */ + { + move_assign(std::move(x.value())); + return *this; + } + + ~value_holder()noexcept{value().~T();} + + friend bool operator==(const value_holder& x,const value_holder& y) + noexcept(is_nothrow_equality_comparable::value) + { + return x.equal(y.value()); + } + +private: + template<typename Allocator> + void copy(Allocator& al,const T& x){copy(al,x,is_copy_constructible{});} + + template<typename Allocator> + void copy(Allocator& al,const T& x,std::true_type) + { + std::allocator_traits<Allocator>::construct(al,data(),x); + } + + template<typename Allocator> + void copy(Allocator&,const T&,std::false_type) + { + throw not_copy_constructible{typeid(T)}; + } + + void copy(const T& x){copy(x,is_copy_constructible{});} + + void copy(const T& x,std::true_type) + { + ::new (data()) T(x); + } + + void copy(const T&,std::false_type) + { + throw not_copy_constructible{typeid(T)}; + } + + void move_assign(T&& x){move_assign(std::move(x),is_move_assignable{});} + + void move_assign(T&& x,std::true_type) + { + value()=std::move(x); + } + + void move_assign(T&& x,std::false_type) + { + /* emulated assignment */ + + static_assert(is_nothrow_move_constructible::value, + "type should be move assignable or nothrow move constructible"); + + if(data()!=boost::addressof(x)){ + value().~T(); + ::new (data()) T(std::move(x)); + } + } + + bool equal(const T& x)const{return equal(x,is_equality_comparable{});} + + bool equal(const T& x,std::true_type)const + { + return value()==x; + } + + bool equal(const T&,std::false_type)const + { + throw not_equality_comparable{typeid(T)}; + } +}; + +template<typename Allocator> +struct value_holder_allocator_adaptor:Allocator +{ + using traits=std::allocator_traits<Allocator>; + + 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; + + template<typename U> + struct rebind + { + using other=value_holder_allocator_adaptor< + typename traits::template rebind_alloc<U>>; + }; + + value_holder_allocator_adaptor()=default; + value_holder_allocator_adaptor( + const value_holder_allocator_adaptor&)=default; + + template< + typename Allocator2, + typename std::enable_if< + is_constructible<Allocator,Allocator2>::value + >::type* =nullptr + > + value_holder_allocator_adaptor(const Allocator2& x)noexcept:Allocator{x}{} + + template< + typename Allocator2, + typename std::enable_if< + is_constructible<Allocator,Allocator2>::value + >::type* =nullptr + > + value_holder_allocator_adaptor( + const value_holder_allocator_adaptor<Allocator2>& x)noexcept: + Allocator{static_cast<const Allocator2&>(x)}{} + + value_holder_allocator_adaptor& operator=( + const value_holder_allocator_adaptor&)=default; + + template<typename T,typename... Args> + void construct(T* p,Args&&... args) + { + ::new ((void*)p) T(std::forward<Args>(args)...); + } + + template<typename T,typename... Args> + void construct(value_holder<T>* p,Args&&... args) + { + ::new ((void*)p) value_holder<T>( + static_cast<Allocator&>(*this),std::forward<Args>(args)...); + } + + template<typename T> + void destroy(T* p) + { + p->~T(); + } + + template<typename T> + void destroy(value_holder<T>* p) + { + traits::destroy( + static_cast<Allocator&>(*this), + reinterpret_cast<T*>(static_cast<value_holder_base<T>*>(p))); + } +}; + +} /* namespace poly_collection::detail */ + +} /* namespace poly_collection */ + +} /* namespace boost */ + +#endif |