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)
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 * - value_holder ctors accept a first allocator arg passed by
44 * boost::poly_collection::detail::allocator_adaptor, for purposes
47 * A pointer to value_holder_base<T> can be reinterpret_cast'ed to T*.
48 * Emplacing is explicitly signalled with value_holder_emplacing_ctor to
49 * protect us from greedy T's constructible from anything (like
50 * boost::type_erasure::any).
53 struct value_holder_emplacing_ctor_t{};
54 constexpr value_holder_emplacing_ctor_t value_holder_emplacing_ctor=
55 value_holder_emplacing_ctor_t();
58 class value_holder_base
61 typename std::aligned_storage<sizeof(T),alignof(T)>::type s;
65 class value_holder:public value_holder_base<T>
68 using enable_if_not_emplacing_ctor_t=typename std::enable_if<
70 typename std::decay<U>::type,value_holder_emplacing_ctor_t
74 using is_nothrow_move_constructible=std::is_nothrow_move_constructible<T>;
75 using is_copy_constructible=std::is_copy_constructible<T>;
76 using is_nothrow_copy_constructible=std::is_nothrow_copy_constructible<T>;
77 using is_move_assignable=std::is_move_assignable<T>;
78 using is_nothrow_move_assignable=std::is_nothrow_move_assignable<T>;
79 using is_equality_comparable=detail::is_equality_comparable<T>;
80 using is_nothrow_equality_comparable=
81 detail::is_nothrow_equality_comparable<T>;
83 T* data()noexcept{return reinterpret_cast<T*>(&this->s);}
84 const T* data()const noexcept
85 {return reinterpret_cast<const T*>(&this->s);}
87 T& value()noexcept{return *static_cast<T*>(data());}
88 const T& value()const noexcept{return *static_cast<const T*>(data());}
93 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
95 value_holder(Allocator& al,const T& x)
96 noexcept(is_nothrow_copy_constructible::value)
100 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
102 value_holder(Allocator& al,T&& x)
103 noexcept(is_nothrow_move_constructible::value)
104 {std::allocator_traits<Allocator>::construct(al,data(),std::move(x));}
106 typename Allocator,typename... Args,
107 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
109 value_holder(Allocator& al,value_holder_emplacing_ctor_t,Args&&... args)
110 {std::allocator_traits<Allocator>::construct(
111 al,data(),std::forward<Args>(args)...);}
114 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
116 value_holder(Allocator& al,const value_holder& x)
117 noexcept(is_nothrow_copy_constructible::value)
118 {copy(al,x.value());}
121 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
123 value_holder(Allocator& al,value_holder&& x)
124 noexcept(is_nothrow_move_constructible::value)
125 {std::allocator_traits<Allocator>::construct(
126 al,data(),std::move(x.value()));}
128 /* stdlib implementations in current use are notoriously lacking at
129 * complying with [container.requirements.general]/3, so we keep the
130 * following to make their life easier.
133 value_holder(const T& x)
134 noexcept(is_nothrow_copy_constructible::value)
137 noexcept(is_nothrow_move_constructible::value)
138 {::new ((void*)data()) T(std::move(x));}
139 template<typename... Args>
140 value_holder(value_holder_emplacing_ctor_t,Args&&... args)
141 {::new ((void*)data()) T(std::forward<Args>(args)...);}
142 value_holder(const value_holder& x)
143 noexcept(is_nothrow_copy_constructible::value)
145 value_holder(value_holder&& x)
146 noexcept(is_nothrow_move_constructible::value)
147 {::new ((void*)data()) T(std::move(x.value()));}
149 value_holder& operator=(const value_holder& x)=delete;
150 value_holder& operator=(value_holder&& x)
151 noexcept(is_nothrow_move_assignable::value||!is_move_assignable::value)
152 /* if 2nd clause: nothrow move constructibility required */
154 move_assign(std::move(x.value()));
158 ~value_holder()noexcept{value().~T();}
160 friend bool operator==(const value_holder& x,const value_holder& y)
161 noexcept(is_nothrow_equality_comparable::value)
163 return x.equal(y.value());
167 template<typename Allocator>
168 void copy(Allocator& al,const T& x){copy(al,x,is_copy_constructible{});}
170 template<typename Allocator>
171 void copy(Allocator& al,const T& x,std::true_type)
173 std::allocator_traits<Allocator>::construct(al,data(),x);
176 template<typename Allocator>
177 void copy(Allocator&,const T&,std::false_type)
179 throw not_copy_constructible{typeid(T)};
182 void copy(const T& x){copy(x,is_copy_constructible{});}
184 void copy(const T& x,std::true_type)
189 void copy(const T&,std::false_type)
191 throw not_copy_constructible{typeid(T)};
194 void move_assign(T&& x){move_assign(std::move(x),is_move_assignable{});}
196 void move_assign(T&& x,std::true_type)
198 value()=std::move(x);
201 void move_assign(T&& x,std::false_type)
203 /* emulated assignment */
205 static_assert(is_nothrow_move_constructible::value,
206 "type should be move assignable or nothrow move constructible");
208 if(data()!=boost::addressof(x)){
210 ::new (data()) T(std::move(x));
214 bool equal(const T& x)const{return equal(x,is_equality_comparable{});}
216 bool equal(const T& x,std::true_type)const
221 bool equal(const T&,std::false_type)const
223 throw not_equality_comparable{typeid(T)};
227 } /* namespace poly_collection::detail */
229 } /* namespace poly_collection */
231 } /* namespace boost */