]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/poly_collection/detail/value_holder.hpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / boost / poly_collection / detail / value_holder.hpp
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)
5 *
6 * See http://www.boost.org/libs/poly_collection for library home page.
7 */
8
9 #ifndef BOOST_POLY_COLLECTION_DETAIL_VALUE_HOLDER_HPP
10 #define BOOST_POLY_COLLECTION_DETAIL_VALUE_HOLDER_HPP
11
12 #if defined(_MSC_VER)
13 #pragma once
14 #endif
15
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>
21 #include <new>
22 #include <memory>
23 #include <type_traits>
24 #include <utility>
25
26 namespace boost{
27
28 namespace poly_collection{
29
30 namespace detail{
31
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
45 * explained there.
46 *
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).
51 */
52
53 struct value_holder_emplacing_ctor_t{};
54 constexpr value_holder_emplacing_ctor_t value_holder_emplacing_ctor=
55 value_holder_emplacing_ctor_t();
56
57 template<typename T>
58 class value_holder_base
59 {
60 protected:
61 typename std::aligned_storage<sizeof(T),alignof(T)>::type s;
62 };
63
64 template<typename T>
65 class value_holder:public value_holder_base<T>
66 {
67 template<typename U>
68 using enable_if_not_emplacing_ctor_t=typename std::enable_if<
69 !std::is_same<
70 typename std::decay<U>::type,value_holder_emplacing_ctor_t
71 >::value
72 >::type*;
73
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>;
82
83 T* data()noexcept{return reinterpret_cast<T*>(&this->s);}
84 const T* data()const noexcept
85 {return reinterpret_cast<const T*>(&this->s);}
86
87 T& value()noexcept{return *static_cast<T*>(data());}
88 const T& value()const noexcept{return *static_cast<const T*>(data());}
89
90 public:
91 template<
92 typename Allocator,
93 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
94 >
95 value_holder(Allocator& al,const T& x)
96 noexcept(is_nothrow_copy_constructible::value)
97 {copy(al,x);}
98 template<
99 typename Allocator,
100 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
101 >
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));}
105 template<
106 typename Allocator,typename... Args,
107 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
108 >
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)...);}
112 template<
113 typename Allocator,
114 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
115 >
116 value_holder(Allocator& al,const value_holder& x)
117 noexcept(is_nothrow_copy_constructible::value)
118 {copy(al,x.value());}
119 template<
120 typename Allocator,
121 enable_if_not_emplacing_ctor_t<Allocator> =nullptr
122 >
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()));}
127
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.
131 */
132
133 value_holder(const T& x)
134 noexcept(is_nothrow_copy_constructible::value)
135 {copy(x);}
136 value_holder(T&& x)
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)
144 {copy(x.value());}
145 value_holder(value_holder&& x)
146 noexcept(is_nothrow_move_constructible::value)
147 {::new ((void*)data()) T(std::move(x.value()));}
148
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 */
153 {
154 move_assign(std::move(x.value()));
155 return *this;
156 }
157
158 ~value_holder()noexcept{value().~T();}
159
160 friend bool operator==(const value_holder& x,const value_holder& y)
161 noexcept(is_nothrow_equality_comparable::value)
162 {
163 return x.equal(y.value());
164 }
165
166 private:
167 template<typename Allocator>
168 void copy(Allocator& al,const T& x){copy(al,x,is_copy_constructible{});}
169
170 template<typename Allocator>
171 void copy(Allocator& al,const T& x,std::true_type)
172 {
173 std::allocator_traits<Allocator>::construct(al,data(),x);
174 }
175
176 template<typename Allocator>
177 void copy(Allocator&,const T&,std::false_type)
178 {
179 throw not_copy_constructible{typeid(T)};
180 }
181
182 void copy(const T& x){copy(x,is_copy_constructible{});}
183
184 void copy(const T& x,std::true_type)
185 {
186 ::new (data()) T(x);
187 }
188
189 void copy(const T&,std::false_type)
190 {
191 throw not_copy_constructible{typeid(T)};
192 }
193
194 void move_assign(T&& x){move_assign(std::move(x),is_move_assignable{});}
195
196 void move_assign(T&& x,std::true_type)
197 {
198 value()=std::move(x);
199 }
200
201 void move_assign(T&& x,std::false_type)
202 {
203 /* emulated assignment */
204
205 static_assert(is_nothrow_move_constructible::value,
206 "type should be move assignable or nothrow move constructible");
207
208 if(data()!=boost::addressof(x)){
209 value().~T();
210 ::new (data()) T(std::move(x));
211 }
212 }
213
214 bool equal(const T& x)const{return equal(x,is_equality_comparable{});}
215
216 bool equal(const T& x,std::true_type)const
217 {
218 return value()==x;
219 }
220
221 bool equal(const T&,std::false_type)const
222 {
223 throw not_equality_comparable{typeid(T)};
224 }
225 };
226
227 } /* namespace poly_collection::detail */
228
229 } /* namespace poly_collection */
230
231 } /* namespace boost */
232
233 #endif