1 /* Copyright 2016-2017 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)
6 * See http://www.boost.org/libs/poly_collection for library home page.
9 #ifndef BOOST_POLY_COLLECTION_DETAIL_VALUE_HOLDER_HPP
10 #define BOOST_POLY_COLLECTION_DETAIL_VALUE_HOLDER_HPP
16 #include <boost/core/addressof.hpp>
17 #include <boost/poly_collection/detail/is_constructible.hpp>
18 #include <boost/poly_collection/detail/is_equality_comparable.hpp>
19 #include <boost/poly_collection/detail/is_nothrow_eq_comparable.hpp>
20 #include <boost/poly_collection/exception.hpp>
23 #include <type_traits>
28 namespace poly_collection{
32 /* Segments of a poly_collection maintain vectors of value_holder<T>
33 * rather than directly T. This serves several purposes:
34 * - value_holder<T> is copy constructible and equality comparable even if T
35 * is not: executing the corresponding op results in a reporting exception
36 * being thrown. This allows the segment to offer its full virtual
37 * interface regardless of the properties of the concrete class contained.
38 * - value_holder<T> emulates move assignment when T is not move assignable
39 * (nothrow move constructibility required); this happens most notably with
40 * lambda functions, whose assignment operator is deleted by standard
41 * mandate [expr.prim.lambda]/20 even if the compiler generated one would
42 * work (capture by value).
43 * - To comply with [container.requirements.general]/3 (or, more precisely,
44 * its natural extension to polymorphic containers), value_holder ctors
45 * accept a first allocator arg passed by value_holder_allocator_adaptor,
46 * which must therefore be used in the vectors of value_holder's.
48 * A pointer to value_holder_base<T> can be reinterpret_cast'ed to T*.
49 * Emplacing is explicitly signalled with value_holder_emplacing_ctor to
50 * protect us from greedy T's constructible from anything (like
51 * boost::type_erasure::any).
54 struct value_holder_emplacing_ctor_t{};
55 constexpr value_holder_emplacing_ctor_t value_holder_emplacing_ctor=
56 value_holder_emplacing_ctor_t();
59 class value_holder_base
62 typename std::aligned_storage<sizeof(T),alignof(T)>::type s;
66 class value_holder:public value_holder_base<T>
69 using enable_if_not_emplacing_ctor_t=typename std::enable_if<
71 typename std::decay<U>::type,value_holder_emplacing_ctor_t
75 using is_nothrow_move_constructible=std::is_nothrow_move_constructible<T>;
76 using is_copy_constructible=std::is_copy_constructible<T>;
77 using is_nothrow_copy_constructible=std::is_nothrow_copy_constructible<T>;
78 using is_move_assignable=std::is_move_assignable<T>;
79 using is_nothrow_move_assignable=std::is_nothrow_move_assignable<T>;
80 using is_equality_comparable=detail::is_equality_comparable<T>;
81 using is_nothrow_equality_comparable=
82 detail::is_nothrow_equality_comparable<T>;
84 T* data()noexcept{return reinterpret_cast<T*>(&this->s);}
85 const T* data()const noexcept
86 {return reinterpret_cast<const T*>(&this->s);}
88 T& value()noexcept{return *static_cast<T*>(data());}
89 const T& value()const noexcept{return *static_cast<const T*>(data());}
94 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
96 value_holder(Allocator& al,const T& x)
97 noexcept(is_nothrow_copy_constructible::value)
101 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
103 value_holder(Allocator& al,T&& x)
104 noexcept(is_nothrow_move_constructible::value)
105 {std::allocator_traits<Allocator>::construct(al,data(),std::move(x));}
107 typename Allocator,typename... Args,
108 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
110 value_holder(Allocator& al,value_holder_emplacing_ctor_t,Args&&... args)
111 {std::allocator_traits<Allocator>::construct(
112 al,data(),std::forward<Args>(args)...);}
115 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
117 value_holder(Allocator& al,const value_holder& x)
118 noexcept(is_nothrow_copy_constructible::value)
119 {copy(al,x.value());}
122 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
124 value_holder(Allocator& al,value_holder&& x)
125 noexcept(is_nothrow_move_constructible::value)
126 {std::allocator_traits<Allocator>::construct(
127 al,data(),std::move(x.value()));}
129 /* stdlib implementations in current use are notoriously lacking at
130 * complying with [container.requirements.general]/3, so we keep the
131 * following to make their life easier.
134 value_holder(const T& x)
135 noexcept(is_nothrow_copy_constructible::value)
138 noexcept(is_nothrow_move_constructible::value)
139 {::new ((void*)data()) T(std::move(x));}
140 template<typename... Args>
141 value_holder(value_holder_emplacing_ctor_t,Args&&... args)
142 {::new ((void*)data()) T(std::forward<Args>(args)...);}
143 value_holder(const value_holder& x)
144 noexcept(is_nothrow_copy_constructible::value)
146 value_holder(value_holder&& x)
147 noexcept(is_nothrow_move_constructible::value)
148 {::new ((void*)data()) T(std::move(x.value()));}
150 value_holder& operator=(const value_holder& x)=delete;
151 value_holder& operator=(value_holder&& x)
152 noexcept(is_nothrow_move_assignable::value||!is_move_assignable::value)
153 /* if 2nd clause: nothrow move constructibility required */
155 move_assign(std::move(x.value()));
159 ~value_holder()noexcept{value().~T();}
161 friend bool operator==(const value_holder& x,const value_holder& y)
162 noexcept(is_nothrow_equality_comparable::value)
164 return x.equal(y.value());
168 template<typename Allocator>
169 void copy(Allocator& al,const T& x){copy(al,x,is_copy_constructible{});}
171 template<typename Allocator>
172 void copy(Allocator& al,const T& x,std::true_type)
174 std::allocator_traits<Allocator>::construct(al,data(),x);
177 template<typename Allocator>
178 void copy(Allocator&,const T&,std::false_type)
180 throw not_copy_constructible{typeid(T)};
183 void copy(const T& x){copy(x,is_copy_constructible{});}
185 void copy(const T& x,std::true_type)
190 void copy(const T&,std::false_type)
192 throw not_copy_constructible{typeid(T)};
195 void move_assign(T&& x){move_assign(std::move(x),is_move_assignable{});}
197 void move_assign(T&& x,std::true_type)
199 value()=std::move(x);
202 void move_assign(T&& x,std::false_type)
204 /* emulated assignment */
206 static_assert(is_nothrow_move_constructible::value,
207 "type should be move assignable or nothrow move constructible");
209 if(data()!=boost::addressof(x)){
211 ::new (data()) T(std::move(x));
215 bool equal(const T& x)const{return equal(x,is_equality_comparable{});}
217 bool equal(const T& x,std::true_type)const
222 bool equal(const T&,std::false_type)const
224 throw not_equality_comparable{typeid(T)};
228 template<typename Allocator>
229 struct value_holder_allocator_adaptor:Allocator
231 using traits=std::allocator_traits<Allocator>;
233 using value_type=typename traits::value_type;
234 using size_type=typename traits::size_type;
235 using difference_type=typename traits::difference_type;
236 using pointer=typename traits::pointer;
237 using const_pointer=typename traits::const_pointer;
238 using void_pointer=typename traits::void_pointer;
239 using const_void_pointer=typename traits::const_void_pointer;
240 using propagate_on_container_copy_assignment=
241 typename traits::propagate_on_container_copy_assignment;
242 using propagate_on_container_move_assignment=
243 typename traits::propagate_on_container_move_assignment;
244 using propagate_on_container_swap=
245 typename traits::propagate_on_container_swap;
250 using other=value_holder_allocator_adaptor<
251 typename traits::template rebind_alloc<U>>;
254 value_holder_allocator_adaptor()=default;
255 value_holder_allocator_adaptor(
256 const value_holder_allocator_adaptor&)=default;
260 typename std::enable_if<
261 is_constructible<Allocator,Allocator2>::value
264 value_holder_allocator_adaptor(const Allocator2& x)noexcept:Allocator{x}{}
268 typename std::enable_if<
269 is_constructible<Allocator,Allocator2>::value
272 value_holder_allocator_adaptor(
273 const value_holder_allocator_adaptor<Allocator2>& x)noexcept:
274 Allocator{static_cast<const Allocator2&>(x)}{}
276 value_holder_allocator_adaptor& operator=(
277 const value_holder_allocator_adaptor&)=default;
279 template<typename T,typename... Args>
280 void construct(T* p,Args&&... args)
282 ::new ((void*)p) T(std::forward<Args>(args)...);
285 template<typename T,typename... Args>
286 void construct(value_holder<T>* p,Args&&... args)
288 ::new ((void*)p) value_holder<T>(
289 static_cast<Allocator&>(*this),std::forward<Args>(args)...);
299 void destroy(value_holder<T>* p)
302 static_cast<Allocator&>(*this),
303 reinterpret_cast<T*>(static_cast<value_holder_base<T>*>(p)));
307 } /* namespace poly_collection::detail */
309 } /* namespace poly_collection */
311 } /* namespace boost */