aligator  0.14.0
A primal-dual augmented Lagrangian-type solver for nonlinear trajectory optimization.
 
Loading...
Searching...
No Matches
polymorphic_cxx14.h
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
36
37namespace xyz {
38template <class T>
40} // namespace xyz
41#endif // XYZ_IN_PLACE_TYPE_DEFINED
42
43#ifndef XYZ_UNREACHABLE_DEFINED
44#define XYZ_UNREACHABLE_DEFINED
45
46namespace xyz {
47[[noreturn]] inline void unreachable() { // LCOV_EXCL_LINE
48#if (__cpp_lib_unreachable >= 202202L)
49 std::unreachable(); // LCOV_EXCL_LINE
50#elif defined(_MSC_VER)
51 __assume(false); // LCOV_EXCL_LINE
52#else
53 __builtin_unreachable(); // LCOV_EXCL_LINE
54#endif
55}
56} // namespace xyz
57#endif // XYZ_UNREACHABLE_DEFINED
58
59#ifndef XYZ_EMPTY_BASE_DEFINED
60#define XYZ_EMPTY_BASE_DEFINED
61
62// This is a helper class to allow empty base class optimization.
63// This implementation is duplicated in compatibility/in_place_type_cxx14.h.
64// These implementations must be kept in sync.
65// We duplicate implementations to allow this header to work as a single
66// include. https://godbolt.org needs single-file includes.
67namespace xyz {
68namespace detail {
69template <class T, bool CanBeEmptyBaseClass =
70 std::is_empty<T>::value && !std::is_final<T>::value>
72 protected:
74
75 empty_base_optimization(const T& t) : t_(t) {}
76
77 empty_base_optimization(T&& t) : t_(std::move(t)) {}
78
79 T& get() noexcept { return t_; }
80
81 const T& get() const noexcept { return t_; }
82
83 T t_;
84};
85
86template <class T>
87class empty_base_optimization<T, true> : private T {
88 protected:
90
91 empty_base_optimization(const T& t) : T(t) {}
92
93 empty_base_optimization(T&& t) : T(std::move(t)) {}
94
95 T& get() noexcept { return *this; }
96
97 const T& get() const noexcept { return *this; }
98};
99} // namespace detail
100} // namespace xyz
101#endif // XYZ_EMPTY_BASE_DEFINED
102
103namespace xyz {
104
105template <class T, class A = std::allocator<T>>
106class polymorphic : private detail::empty_base_optimization<A> {
107 struct control_block {
108 using allocator_traits = std::allocator_traits<A>;
109 typename allocator_traits::pointer p_;
110
111 virtual ~control_block() = default;
112 virtual void destroy(A& alloc) = 0;
113 virtual control_block* clone(const A& alloc) = 0;
114 virtual control_block* move(const A& alloc) = 0;
115 };
116
117 template <class U>
118 class direct_control_block final : public control_block {
119 union uninitialized_storage {
120 U u_;
121
122 uninitialized_storage() {}
123
124 ~uninitialized_storage() {}
125 } storage_;
126
127 using cb_allocator = typename std::allocator_traits<
128 A>::template rebind_alloc<direct_control_block<U>>;
129 using cb_alloc_traits = std::allocator_traits<cb_allocator>;
130
131 public:
132 template <class... Ts>
133 direct_control_block(const A& alloc, Ts&&... ts) {
134 cb_allocator cb_alloc(alloc);
135 cb_alloc_traits::construct(cb_alloc, std::addressof(storage_.u_),
136 std::forward<Ts>(ts)...);
137 control_block::p_ = std::addressof(storage_.u_);
138 }
139
140 control_block* clone(const A& alloc) override {
141 cb_allocator cb_alloc(alloc);
142 auto mem = cb_alloc_traits::allocate(cb_alloc, 1);
143 try {
144 cb_alloc_traits::construct(cb_alloc, mem, alloc, storage_.u_);
145 return mem;
146 } catch (...) {
147 cb_alloc_traits::deallocate(cb_alloc, mem, 1);
148 throw;
149 }
150 }
151
152 control_block* move(const A& alloc) override {
153 cb_allocator cb_alloc(alloc);
154 auto mem = cb_alloc_traits::allocate(cb_alloc, 1);
155 try {
156 cb_alloc_traits::construct(cb_alloc, mem, alloc,
157 std::move(storage_.u_));
158 return mem;
159 } catch (...) {
160 cb_alloc_traits::deallocate(cb_alloc, mem, 1);
161 throw;
162 }
163 }
164
165 void destroy(A& alloc) override {
166 cb_allocator cb_alloc(alloc);
167 cb_alloc_traits::destroy(cb_alloc, std::addressof(storage_.u_));
168 cb_alloc_traits::deallocate(cb_alloc, this, 1);
169 }
170 };
171
172 control_block* cb_;
173 using allocator_traits = std::allocator_traits<A>;
174 using alloc_base = detail::empty_base_optimization<A>;
175
176 template <class U, class... Ts>
177 control_block* create_control_block(Ts&&... ts) const {
178 using cb_allocator = typename std::allocator_traits<
179 A>::template rebind_alloc<direct_control_block<U>>;
180 cb_allocator cb_alloc(alloc_base::get());
181 using cb_alloc_traits = std::allocator_traits<cb_allocator>;
182 auto mem = cb_alloc_traits::allocate(cb_alloc, 1);
183 try {
184 cb_alloc_traits::construct(cb_alloc, mem, alloc_base::get(),
185 std::forward<Ts>(ts)...);
186 return mem;
187 } catch (...) {
188 cb_alloc_traits::deallocate(cb_alloc, mem, 1);
189 throw;
190 }
191 }
192
193 public:
194 using value_type = T;
195 using allocator_type = A;
196 using pointer = typename allocator_traits::pointer;
197 using const_pointer = typename allocator_traits::const_pointer;
198
199 template <typename TT = T,
200 typename std::enable_if<std::is_default_constructible<TT>::value,
201 int>::type = 0>
202 polymorphic(std::allocator_arg_t, const A& alloc) : alloc_base(alloc) {
203 cb_ = create_control_block<T>();
204 }
205
206 template <typename TT = T,
207 typename std::enable_if<std::is_default_constructible<TT>::value,
208 int>::type = 0,
209 typename AA = A,
210 typename std::enable_if<std::is_default_constructible<AA>::value,
211 int>::type = 0>
212 polymorphic() : alloc_base() {
213 cb_ = create_control_block<T>();
214 }
215
216 template <
217 class U, class... Ts,
218 typename std::enable_if<std::is_constructible<U, Ts&&...>::value,
219 int>::type = 0,
220 typename std::enable_if<std::is_copy_constructible<U>::value, int>::type =
221 0,
222 typename std::enable_if<std::is_base_of<T, U>::value, int>::type = 0>
223 polymorphic(std::allocator_arg_t, const A& alloc, in_place_type_t<U>,
224 Ts&&... ts)
225 : alloc_base(alloc) {
226 cb_ = create_control_block<U>(std::forward<Ts>(ts)...);
227 }
228
229 template <
230 class U, class I, class... Ts,
231 typename std::enable_if<
232 std::is_constructible<U, std::initializer_list<I>, Ts&&...>::value,
233 int>::type = 0,
234 typename std::enable_if<std::is_copy_constructible<U>::value, int>::type =
235 0,
236 typename std::enable_if<std::is_base_of<T, U>::value, int>::type = 0>
237 polymorphic(std::allocator_arg_t, const A& alloc, in_place_type_t<U>,
238 std::initializer_list<I> ilist, Ts&&... ts)
239 : alloc_base(alloc) {
240 cb_ = create_control_block<T>(ilist, std::forward<Ts>(ts)...);
241 }
242
243 template <
244 class U, class I, class... Ts,
245 typename std::enable_if<
246 std::is_constructible<U, std::initializer_list<I>, Ts&&...>::value,
247 int>::type = 0,
248 typename std::enable_if<std::is_copy_constructible<U>::value, int>::type =
249 0,
250 typename std::enable_if<std::is_base_of<T, U>::value, int>::type = 0,
251 typename AA = A,
252 typename std::enable_if<std::is_default_constructible<AA>::value,
253 int>::type = 0>
254 explicit polymorphic(in_place_type_t<U>, std::initializer_list<I> ilist,
255 Ts&&... ts)
256 : polymorphic(std::allocator_arg, A(), in_place_type_t<U>{}, ilist,
257 std::forward<Ts>(ts)...) {}
258
259 template <
260 class U, class... Ts,
261 typename std::enable_if<std::is_constructible<U, Ts&&...>::value,
262 int>::type = 0,
263 typename std::enable_if<std::is_copy_constructible<U>::value, int>::type =
264 0,
265 typename std::enable_if<std::is_base_of<T, U>::value, int>::type = 0,
266 typename AA = A,
267 typename std::enable_if<std::is_default_constructible<AA>::value,
268 int>::type = 0>
269 explicit polymorphic(in_place_type_t<U>, Ts&&... ts)
270 : polymorphic(std::allocator_arg, A(), in_place_type_t<U>{},
271 std::forward<Ts>(ts)...) {}
272
273 template <
274 class U,
275 typename std::enable_if<
276 !std::is_same<polymorphic,
277 typename std::remove_cv<typename std::remove_reference<
278 U>::type>::type>::value,
279 int>::type = 0,
280 typename std::enable_if<
281 std::is_copy_constructible<typename std::remove_cv<
282 typename std::remove_reference<U>::type>::type>::value,
283 int>::type = 0,
284 typename std::enable_if<
285 std::is_base_of<
286 T, typename std::remove_cv<
287 typename std::remove_reference<U>::type>::type>::value,
288 int>::type = 0>
289 explicit polymorphic(std::allocator_arg_t, const A& alloc, U&& u)
290 : polymorphic(std::allocator_arg_t{}, alloc,
291 in_place_type_t<typename std::remove_cv<
292 typename std::remove_reference<U>::type>::type>{},
293 std::forward<U>(u)) {}
294
295 template <
296 class U,
297 typename std::enable_if<
298 !std::is_same<polymorphic,
299 typename std::remove_cv<typename std::remove_reference<
300 U>::type>::type>::value,
301 int>::type = 0,
302 typename std::enable_if<
303 std::is_copy_constructible<typename std::remove_cv<
304 typename std::remove_reference<U>::type>::type>::value,
305 int>::type = 0,
306 typename std::enable_if<
307 std::is_base_of<
308 T, typename std::remove_cv<
309 typename std::remove_reference<U>::type>::type>::value,
310 int>::type = 0>
311 /* explicit */ polymorphic(U&& u)
312 : polymorphic(std::allocator_arg_t{}, A{},
313 in_place_type_t<typename std::remove_cv<
314 typename std::remove_reference<U>::type>::type>{},
315 std::forward<U>(u)) {}
316
317 polymorphic(std::allocator_arg_t, const A& alloc, const polymorphic& other)
318 : alloc_base(alloc) {
319 if (!other.valueless_after_move()) {
320 cb_ = other.cb_->clone(alloc_base::get());
321 } else {
322 cb_ = nullptr;
323 }
324 }
325
327 : polymorphic(std::allocator_arg,
328 allocator_traits::select_on_container_copy_construction(
329 other.get_allocator()),
330 other) {}
331
333 std::allocator_arg_t, const A& alloc,
334 polymorphic&& other) noexcept(allocator_traits::is_always_equal::value)
335 : alloc_base(alloc) {
336 if (allocator_traits::propagate_on_container_copy_assignment::value) {
337 cb_ = other.cb_;
338 other.cb_ = nullptr;
339 } else {
340 if (get_allocator() == other.get_allocator()) {
341 cb_ = other.cb_;
342 other.cb_ = nullptr;
343 } else {
344 if (!other.valueless_after_move()) {
345 cb_ = other.cb_->move(alloc_base::get());
346 } else {
347 cb_ = nullptr;
348 }
349 }
350 }
351 }
352
353 polymorphic(polymorphic&& other) noexcept
354 : polymorphic(std::allocator_arg, other.get_allocator(),
355 std::move(other)) {}
356
357 ~polymorphic() { reset(); }
358
359 constexpr polymorphic& operator=(const polymorphic& other) {
360 if (this == &other) return *this;
361
362 // Check to see if the allocators need to be updated.
363 // We defer actually updating the allocator until later because it may be
364 // needed to delete the current control block.
365 bool update_alloc =
366 allocator_traits::propagate_on_container_copy_assignment::value;
367
368 if (other.valueless_after_move()) {
369 reset();
370 } else {
371 // Constructing a new control block could throw so we need to defer
372 // resetting or updating allocators until this is done.
373 auto tmp = other.cb_->clone(update_alloc ? other.alloc_base::get()
374 : alloc_base::get());
375 reset();
376 cb_ = tmp;
377 }
378 if (update_alloc) {
379 alloc_base::get() = other.alloc_base::get();
380 }
381 return *this;
382 }
383
384 constexpr polymorphic& operator=(polymorphic&& other) noexcept(
385 allocator_traits::propagate_on_container_move_assignment::value ||
386 allocator_traits::is_always_equal::value) {
387 if (this == &other) return *this;
388
389 // Check to see if the allocators need to be updated.
390 // We defer actually updating the allocator until later because it may be
391 // needed to delete the current control block.
392 bool update_alloc =
393 allocator_traits::propagate_on_container_move_assignment::value;
394
395 if (other.valueless_after_move()) {
396 reset();
397 } else {
398 if (alloc_base::get() == other.alloc_base::get()) {
399 std::swap(cb_, other.cb_);
400 other.reset();
401 } else {
402 // Constructing a new control block could throw so we need to defer
403 // resetting or updating allocators until this is done.
404 auto tmp = other.cb_->move(update_alloc ? other.alloc_base::get()
405 : alloc_base::get());
406 reset();
407 cb_ = tmp;
408 }
409 }
410
411 if (update_alloc) {
412 alloc_base::get() = other.alloc_base::get();
413 }
414 return *this;
415 }
416
417 [[nodiscard]] pointer operator->() noexcept {
418 assert(!valueless_after_move()); // LCOV_EXCL_LINE
419 return cb_->p_;
420 }
421
422 [[nodiscard]] const_pointer operator->() const noexcept {
423 assert(!valueless_after_move()); // LCOV_EXCL_LINE
424 return cb_->p_;
425 }
426
427 [[nodiscard]] T& operator*() noexcept {
428 assert(!valueless_after_move()); // LCOV_EXCL_LINE
429 return *cb_->p_;
430 }
431
432 [[nodiscard]] const T& operator*() const noexcept {
433 assert(!valueless_after_move()); // LCOV_EXCL_LINE
434 return *cb_->p_;
435 }
436
437 [[nodiscard]] bool valueless_after_move() const noexcept {
438 return cb_ == nullptr;
439 }
440
441 allocator_type get_allocator() const noexcept { return alloc_base::get(); }
442
443 void swap(polymorphic& other) noexcept(
444 std::allocator_traits<A>::propagate_on_container_swap::value ||
445 std::allocator_traits<A>::is_always_equal::value) {
446 if (allocator_traits::propagate_on_container_swap::value) {
447 // If allocators move with their allocated objects we can swap both.
448 std::swap(alloc_base::get(), other.alloc_base::get());
449 std::swap(cb_, other.cb_);
450 return;
451 } else /* */ {
452 if (alloc_base::get() == other.alloc_base::get()) {
453 std::swap(cb_, other.cb_);
454 } else {
455 unreachable(); // LCOV_EXCL_LINE
456 }
457 }
458 }
459
460 friend void swap(polymorphic& lhs,
461 polymorphic& rhs) noexcept(noexcept(lhs.swap(rhs))) {
462 lhs.swap(rhs);
463 }
464
465 private:
466 void reset() noexcept {
467 if (cb_ != nullptr) {
468 cb_->destroy(alloc_base::get());
469 cb_ = nullptr;
470 }
471 }
472};
473
474} // namespace xyz
475
476#endif // XYZ_POLYMORPHIC_H_
polymorphic(std::allocator_arg_t, const A &alloc)
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)
std::allocator< StageFunctionTpl< Scalar > > allocator_type
polymorphic(in_place_type_t< U >, std::initializer_list< I > ilist, Ts &&... ts)
Definition fwd.hpp:52
void unreachable()