proxsuite-nlp  0.10.0
A primal-dual augmented Lagrangian-type solver for nonlinear programming on manifolds.
Loading...
Searching...
No Matches
polymorphic_cxx14.hpp
Go to the documentation of this file.
1/* Copyright (c) 2016 The Value Types Authors. All Rights Reserved.
2
3Permission is hereby granted, free of charge, to any person obtaining a copy of
4this software and associated documentation files (the "Software"), to deal in
5the Software without restriction, including without limitation the rights to
6use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7the Software, and to permit persons to whom the Software is furnished to do so,
8subject to the following conditions:
9
10The above copyright notice and this permission notice shall be included in all
11copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19==============================================================================*/
20
21#ifndef XYZ_POLYMORPHIC_H_
22#define XYZ_POLYMORPHIC_H_
23
24#include <cassert>
25#include <initializer_list>
26#include <memory>
27#include <type_traits>
28#include <utility>
29
30#ifndef XYZ_POLYMORPHIC_HAS_EXTENDED_CONSTRUCTORS
31#define XYZ_POLYMORPHIC_HAS_EXTENDED_CONSTRUCTORS 1
32#endif // XYZ_POLYMORPHIC_HAS_EXTENDED_CONSTRUCTORS
33
34#ifndef XYZ_IN_PLACE_TYPE_DEFINED
35#define XYZ_IN_PLACE_TYPE_DEFINED
36namespace xyz {
37template <class T>
39} // namespace xyz
40#endif // XYZ_IN_PLACE_TYPE_DEFINED
41
42#ifndef XYZ_UNREACHABLE_DEFINED
43#define XYZ_UNREACHABLE_DEFINED
44namespace xyz {
45[[noreturn]] inline void unreachable() { // LCOV_EXCL_LINE
46#if (__cpp_lib_unreachable >= 202202L)
47 std::unreachable(); // LCOV_EXCL_LINE
48#elif defined(_MSC_VER)
49 __assume(false); // LCOV_EXCL_LINE
50#else
51 __builtin_unreachable(); // LCOV_EXCL_LINE
52#endif
53}
54} // namespace xyz
55#endif // XYZ_UNREACHABLE_DEFINED
56
57#ifndef XYZ_EMPTY_BASE_DEFINED
58#define XYZ_EMPTY_BASE_DEFINED
59// This is a helper class to allow empty base class optimization.
60// This implementation is duplicated in compatibility/in_place_type_cxx14.h.
61// These implementations must be kept in sync.
62// We duplicate implementations to allow this header to work as a single
63// include. https://godbolt.org needs single-file includes.
64namespace xyz {
65namespace detail {
66template <class T, bool CanBeEmptyBaseClass =
67 std::is_empty<T>::value && !std::is_final<T>::value>
69 protected:
71 empty_base_optimization(const T& t) : t_(t) {}
72 empty_base_optimization(T&& t) : t_(std::move(t)) {}
73 T& get() noexcept { return t_; }
74 const T& get() const noexcept { return t_; }
75 T t_;
76};
77
78template <class T>
79class empty_base_optimization<T, true> : private T {
80 protected:
82 empty_base_optimization(const T& t) : T(t) {}
83 empty_base_optimization(T&& t) : T(std::move(t)) {}
84 T& get() noexcept { return *this; }
85 const T& get() const noexcept { return *this; }
86};
87} // namespace detail
88} // namespace xyz
89#endif // XYZ_EMPTY_BASE_DEFINED
90
91namespace xyz {
92namespace detail {
93template <class T, class A>
95 using allocator_traits = std::allocator_traits<A>;
96
97 typename allocator_traits::pointer p_;
98
99 virtual ~control_block() = default;
100 virtual void destroy(A& alloc) = 0;
101 virtual control_block<T, A>* clone(const A& alloc) = 0;
102 virtual control_block<T, A>* move(const A& alloc) = 0;
103};
104
105template <class T, class U, class A>
106class direct_control_block final : public control_block<T, A> {
107 union uninitialized_storage {
108 U u_;
109 uninitialized_storage() {}
110 ~uninitialized_storage() {}
111 } storage_;
112
113 using cb_allocator = typename std::allocator_traits<A>::template rebind_alloc<
115 using cb_alloc_traits = std::allocator_traits<cb_allocator>;
116
117 public:
118 template <class... Ts>
119 direct_control_block(const A& alloc, Ts&&... ts) {
120 cb_allocator cb_alloc(alloc);
121 cb_alloc_traits::construct(cb_alloc, std::addressof(storage_.u_),
122 std::forward<Ts>(ts)...);
123 control_block<T, A>::p_ = std::addressof(storage_.u_);
124 }
125
126 control_block<T, A>* clone(const A& alloc) override {
127 cb_allocator cb_alloc(alloc);
128 auto mem = cb_alloc_traits::allocate(cb_alloc, 1);
129 try {
130 cb_alloc_traits::construct(cb_alloc, mem, alloc, storage_.u_);
131 return mem;
132 } catch (...) {
133 cb_alloc_traits::deallocate(cb_alloc, mem, 1);
134 throw;
135 }
136 }
137
138 control_block<T, A>* move(const A& alloc) override {
139 cb_allocator cb_alloc(alloc);
140 auto mem = cb_alloc_traits::allocate(cb_alloc, 1);
141 try {
142 cb_alloc_traits::construct(cb_alloc, mem, alloc, std::move(storage_.u_));
143 return mem;
144 } catch (...) {
145 cb_alloc_traits::deallocate(cb_alloc, mem, 1);
146 throw;
147 }
148 }
149
150 void destroy(A& alloc) override {
151 cb_allocator cb_alloc(alloc);
152 cb_alloc_traits::destroy(cb_alloc, std::addressof(storage_.u_));
153 cb_alloc_traits::deallocate(cb_alloc, this, 1);
154 }
155};
156
157} // namespace detail
158
159template <class T, class A = std::allocator<T>>
162 cblock_t* cb_;
163
164 using allocator_traits = std::allocator_traits<A>;
166
167 template <class U, class... Ts>
168 cblock_t* create_control_block(Ts&&... ts) const {
169 using cb_allocator = typename std::allocator_traits<
170 A>::template rebind_alloc<detail::direct_control_block<T, U, A>>;
171 cb_allocator cb_alloc(alloc_base::get());
172 using cb_alloc_traits = std::allocator_traits<cb_allocator>;
173 auto mem = cb_alloc_traits::allocate(cb_alloc, 1);
174 try {
175 cb_alloc_traits::construct(cb_alloc, mem, alloc_base::get(),
176 std::forward<Ts>(ts)...);
177 return mem;
178 } catch (...) {
179 cb_alloc_traits::deallocate(cb_alloc, mem, 1);
180 throw;
181 }
182 }
183
184 public:
185 using value_type = T;
186 using allocator_type = A;
187 using pointer = typename allocator_traits::pointer;
188 using const_pointer = typename allocator_traits::const_pointer;
189
190 template <typename TT = T,
191 typename std::enable_if<std::is_default_constructible<TT>::value,
192 int>::type = 0>
193 polymorphic(std::allocator_arg_t, const A& alloc) : alloc_base(alloc) {
194 cb_ = create_control_block<T>();
195 }
196
197 template <typename TT = T,
198 typename std::enable_if<std::is_default_constructible<TT>::value,
199 int>::type = 0,
200 typename AA = A,
201 typename std::enable_if<std::is_default_constructible<AA>::value,
202 int>::type = 0>
204 cb_ = create_control_block<T>();
205 }
206
207 template <
208 class U, class... Ts,
209 typename std::enable_if<std::is_constructible<U, Ts&&...>::value,
210 int>::type = 0,
211 typename std::enable_if<std::is_copy_constructible<U>::value, int>::type =
212 0,
213 typename std::enable_if<std::is_base_of<T, U>::value, int>::type = 0>
214 polymorphic(std::allocator_arg_t, const A& alloc, in_place_type_t<U>,
215 Ts&&... ts)
216 : alloc_base(alloc) {
217 cb_ = create_control_block<U>(std::forward<Ts>(ts)...);
218 }
219
220 template <
221 class U, class I, class... Ts,
222 typename std::enable_if<
223 std::is_constructible<U, std::initializer_list<I>, Ts&&...>::value,
224 int>::type = 0,
225 typename std::enable_if<std::is_copy_constructible<U>::value, int>::type =
226 0,
227 typename std::enable_if<std::is_base_of<T, U>::value, int>::type = 0>
228 polymorphic(std::allocator_arg_t, const A& alloc, in_place_type_t<U>,
229 std::initializer_list<I> ilist, Ts&&... ts)
230 : alloc_base(alloc) {
231 cb_ = create_control_block<T>(ilist, std::forward<Ts>(ts)...);
232 }
233
234 template <
235 class U, class I, class... Ts,
236 typename std::enable_if<
237 std::is_constructible<U, std::initializer_list<I>, Ts&&...>::value,
238 int>::type = 0,
239 typename std::enable_if<std::is_copy_constructible<U>::value, int>::type =
240 0,
241 typename std::enable_if<std::is_base_of<T, U>::value, int>::type = 0,
242 typename AA = A,
243 typename std::enable_if<std::is_default_constructible<AA>::value,
244 int>::type = 0>
245 explicit polymorphic(in_place_type_t<U>, std::initializer_list<I> ilist,
246 Ts&&... ts)
247 : polymorphic(std::allocator_arg, A(), in_place_type_t<U>{}, ilist,
248 std::forward<Ts>(ts)...) {}
249
250 template <
251 class U, class... Ts,
252 typename std::enable_if<std::is_constructible<U, Ts&&...>::value,
253 int>::type = 0,
254 typename std::enable_if<std::is_copy_constructible<U>::value, int>::type =
255 0,
256 typename std::enable_if<std::is_base_of<T, U>::value, int>::type = 0,
257 typename AA = A,
258 typename std::enable_if<std::is_default_constructible<AA>::value,
259 int>::type = 0>
260 explicit polymorphic(in_place_type_t<U>, Ts&&... ts)
261 : polymorphic(std::allocator_arg, A(), in_place_type_t<U>{},
262 std::forward<Ts>(ts)...) {}
263
264 template <
265 class U,
266 typename std::enable_if<
267 !std::is_same<polymorphic,
268 typename std::remove_cv<typename std::remove_reference<
269 U>::type>::type>::value,
270 int>::type = 0,
271 typename std::enable_if<
272 std::is_copy_constructible<typename std::remove_cv<
273 typename std::remove_reference<U>::type>::type>::value,
274 int>::type = 0,
275 typename std::enable_if<
276 std::is_base_of<
277 T, typename std::remove_cv<
278 typename std::remove_reference<U>::type>::type>::value,
279 int>::type = 0>
280 explicit polymorphic(std::allocator_arg_t, const A& alloc, U&& u)
281 : polymorphic(std::allocator_arg_t{}, alloc,
282 in_place_type_t<typename std::remove_cv<
283 typename std::remove_reference<U>::type>::type>{},
284 std::forward<U>(u)) {}
285
286 template <
287 class U,
288 typename std::enable_if<
289 !std::is_same<polymorphic,
290 typename std::remove_cv<typename std::remove_reference<
291 U>::type>::type>::value,
292 int>::type = 0,
293 typename std::enable_if<
294 std::is_copy_constructible<typename std::remove_cv<
295 typename std::remove_reference<U>::type>::type>::value,
296 int>::type = 0,
297 typename std::enable_if<
298 std::is_base_of<
299 T, typename std::remove_cv<
300 typename std::remove_reference<U>::type>::type>::value,
301 int>::type = 0>
303 : polymorphic(std::allocator_arg_t{}, A{},
304 in_place_type_t<typename std::remove_cv<
305 typename std::remove_reference<U>::type>::type>{},
306 std::forward<U>(u)) {}
307
308 polymorphic(std::allocator_arg_t, const A& alloc, const polymorphic& other)
309 : alloc_base(alloc) {
310 if (!other.valueless_after_move()) {
311 cb_ = other.cb_->clone(alloc_base::get());
312 } else {
313 cb_ = nullptr;
314 }
315 }
316
318 : polymorphic(std::allocator_arg,
319 allocator_traits::select_on_container_copy_construction(
320 other.get_allocator()),
321 other) {}
322
324 std::allocator_arg_t, const A& alloc,
325 polymorphic&& other) noexcept(allocator_traits::is_always_equal::value)
326 : alloc_base(alloc) {
327 if (allocator_traits::propagate_on_container_copy_assignment::value) {
328 cb_ = other.cb_;
329 other.cb_ = nullptr;
330 } else {
331 if (get_allocator() == other.get_allocator()) {
332 cb_ = other.cb_;
333 other.cb_ = nullptr;
334 } else {
335 if (!other.valueless_after_move()) {
336 cb_ = other.cb_->move(alloc_base::get());
337 } else {
338 cb_ = nullptr;
339 }
340 }
341 }
342 }
343
344 polymorphic(polymorphic&& other) noexcept
345 : polymorphic(std::allocator_arg, other.get_allocator(),
346 std::move(other)) {}
347
348 ~polymorphic() { reset(); }
349
350 constexpr polymorphic& operator=(const polymorphic& other) {
351 if (this == &other) return *this;
352
353 // Check to see if the allocators need to be updated.
354 // We defer actually updating the allocator until later because it may be
355 // needed to delete the current control block.
356 bool update_alloc =
357 allocator_traits::propagate_on_container_copy_assignment::value;
358
359 if (other.valueless_after_move()) {
360 reset();
361 } else {
362 // Constructing a new control block could throw so we need to defer
363 // resetting or updating allocators until this is done.
364 auto tmp = other.cb_->clone(update_alloc ? other.alloc_base::get()
365 : alloc_base::get());
366 reset();
367 cb_ = tmp;
368 }
369 if (update_alloc) {
370 alloc_base::get() = other.alloc_base::get();
371 }
372 return *this;
373 }
374
375 constexpr polymorphic& operator=(polymorphic&& other) noexcept(
376 allocator_traits::propagate_on_container_move_assignment::value ||
377 allocator_traits::is_always_equal::value) {
378 if (this == &other) return *this;
379
380 // Check to see if the allocators need to be updated.
381 // We defer actually updating the allocator until later because it may be
382 // needed to delete the current control block.
383 bool update_alloc =
384 allocator_traits::propagate_on_container_move_assignment::value;
385
386 if (other.valueless_after_move()) {
387 reset();
388 } else {
389 if (alloc_base::get() == other.alloc_base::get()) {
390 std::swap(cb_, other.cb_);
391 other.reset();
392 } else {
393 // Constructing a new control block could throw so we need to defer
394 // resetting or updating allocators until this is done.
395 auto tmp = other.cb_->move(update_alloc ? other.alloc_base::get()
396 : alloc_base::get());
397 reset();
398 cb_ = tmp;
399 }
400 }
401
402 if (update_alloc) {
403 alloc_base::get() = other.alloc_base::get();
404 }
405 return *this;
406 }
407
408 [[nodiscard]] pointer operator->() noexcept {
409 assert(!valueless_after_move()); // LCOV_EXCL_LINE
410 return cb_->p_;
411 }
412
413 [[nodiscard]] const_pointer operator->() const noexcept {
414 assert(!valueless_after_move()); // LCOV_EXCL_LINE
415 return cb_->p_;
416 }
417
418 [[nodiscard]] T& operator*() noexcept {
419 assert(!valueless_after_move()); // LCOV_EXCL_LINE
420 return *cb_->p_;
421 }
422
423 [[nodiscard]] const T& operator*() const noexcept {
424 assert(!valueless_after_move()); // LCOV_EXCL_LINE
425 return *cb_->p_;
426 }
427
428 [[nodiscard]] bool valueless_after_move() const noexcept {
429 return cb_ == nullptr;
430 }
431
432 allocator_type get_allocator() const noexcept { return alloc_base::get(); }
433
434 void swap(polymorphic& other) noexcept(
435 std::allocator_traits<A>::propagate_on_container_swap::value ||
436 std::allocator_traits<A>::is_always_equal::value) {
437 if (allocator_traits::propagate_on_container_swap::value) {
438 // If allocators move with their allocated objects we can swap both.
439 std::swap(alloc_base::get(), other.alloc_base::get());
440 std::swap(cb_, other.cb_);
441 return;
442 } else /* */ {
443 if (alloc_base::get() == other.alloc_base::get()) {
444 std::swap(cb_, other.cb_);
445 } else {
446 unreachable(); // LCOV_EXCL_LINE
447 }
448 }
449 }
450
451 friend void swap(polymorphic& lhs,
452 polymorphic& rhs) noexcept(noexcept(lhs.swap(rhs))) {
453 lhs.swap(rhs);
454 }
455
456 private:
457 void reset() noexcept {
458 if (cb_ != nullptr) {
459 cb_->destroy(alloc_base::get());
460 cb_ = nullptr;
461 }
462 }
463};
464
465} // namespace xyz
466
467#endif // XYZ_POLYMORPHIC_H_
direct_control_block(const A &alloc, Ts &&... ts)
control_block< T, A > * clone(const A &alloc) override
control_block< T, A > * move(const A &alloc) override
bool valueless_after_move() const noexcept
polymorphic(std::allocator_arg_t, const A &alloc, const polymorphic &other)
typename allocator_traits::pointer pointer
T & operator*() noexcept
polymorphic(std::allocator_arg_t, const A &alloc, in_place_type_t< U >, std::initializer_list< I > ilist, Ts &&... ts)
polymorphic(in_place_type_t< U >, Ts &&... ts)
friend void swap(polymorphic &lhs, polymorphic &rhs) noexcept(noexcept(lhs.swap(rhs)))
polymorphic(std::allocator_arg_t, const A &alloc, in_place_type_t< U >, Ts &&... ts)
polymorphic(const polymorphic &other)
allocator_type get_allocator() const noexcept
const_pointer operator->() const noexcept
pointer operator->() noexcept
void swap(polymorphic &other) noexcept(std::allocator_traits< A >::propagate_on_container_swap::value||std::allocator_traits< A >::is_always_equal::value)
polymorphic(std::allocator_arg_t, const A &alloc, polymorphic &&other) noexcept(allocator_traits::is_always_equal::value)
polymorphic(std::allocator_arg_t, const A &alloc)
polymorphic(polymorphic &&other) noexcept
constexpr polymorphic & operator=(polymorphic &&other) noexcept(allocator_traits::propagate_on_container_move_assignment::value||allocator_traits::is_always_equal::value)
typename allocator_traits::const_pointer const_pointer
constexpr polymorphic & operator=(const polymorphic &other)
const T & operator*() const noexcept
polymorphic(std::allocator_arg_t, const A &alloc, U &&u)
polymorphic(in_place_type_t< U >, std::initializer_list< I > ilist, Ts &&... ts)
Definition fwd.hpp:42
void unreachable()
std::allocator_traits< A > allocator_traits
virtual ~control_block()=default
virtual control_block< T, A > * move(const A &alloc)=0
virtual void destroy(A &alloc)=0
virtual control_block< T, A > * clone(const A &alloc)=0
allocator_traits::pointer p_