]>
Commit | Line | Data |
---|---|---|
92f5a8d4 | 1 | /* Copyright 2016-2018 Joaquin M Lopez Munoz. |
b32b8144 FG |
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). | |
92f5a8d4 TL |
43 | * - value_holder ctors accept a first allocator arg passed by |
44 | * boost::poly_collection::detail::allocator_adaptor, for purposes | |
45 | * explained there. | |
b32b8144 FG |
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 | ||
b32b8144 FG |
227 | } /* namespace poly_collection::detail */ |
228 | ||
229 | } /* namespace poly_collection */ | |
230 | ||
231 | } /* namespace boost */ | |
232 | ||
233 | #endif |