alpaqa 1.0.0a18
Nonconvex constrained optimization
Loading...
Searching...
No Matches
type-erasure.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <alpaqa/export.hpp>
7
8#include <algorithm>
9#include <cassert>
10#include <cstddef>
11#include <exception>
12#include <functional>
13#include <memory>
14#include <new>
15#include <stdexcept>
16#include <type_traits>
17#include <typeinfo>
18#include <utility>
19
20namespace alpaqa::util {
21
22class ALPAQA_EXPORT bad_type_erased_type : public std::logic_error {
23 public:
24 bad_type_erased_type(const std::type_info &actual_type,
25 const std::type_info &requested_type,
26 const std::string &message = "")
27 : std::logic_error{message}, actual_type{actual_type},
28 requested_type{requested_type} {}
29
30 [[nodiscard]] const char *what() const noexcept override {
31 message = "";
32 if (const char *w = std::logic_error::what(); w && *w) {
33 message += w;
34 message += ": ";
35 }
36 message = "Type requested: " + demangled_typename(requested_type) +
37 ", type contained: " + demangled_typename(actual_type);
38 return message.c_str();
39 }
40
41 private:
42 const std::type_info &actual_type;
43 const std::type_info &requested_type;
44 mutable std::string message;
45};
46
47class ALPAQA_EXPORT bad_type_erased_constness : public std::logic_error {
48 public:
50 : std::logic_error{"Non-const method called on a TypeErased object "
51 "that references a const object"} {}
52};
53
54/// Struct that stores the size of a polymorphic object, as well as pointers to
55/// functions to copy, move or destroy the object.
56/// Inherit from this struct to add useful functions.
58
59 template <class>
60 struct required_function; // undefined
61 template <class R, class... Args>
62 struct required_function<R(Args...)> {
63 using type = R (*)(void *self, Args...);
64 };
65 template <class R, class... Args>
67 using type = R (*)(const void *self, Args...);
68 };
69 template <class, class VTable = BasicVTable>
70 struct optional_function; // undefined
71 template <class R, class... Args, class VTable>
72 struct optional_function<R(Args...), VTable> {
73 using type = R (*)(void *self, Args..., const VTable &);
74 };
75 template <class R, class... Args, class VTable>
76 struct optional_function<R(Args...) const, VTable> {
77 using type = R (*)(const void *self, Args..., const VTable &);
78 };
79 /// A required function includes a void pointer to self, in addition to the
80 /// arguments of @p F.
81 template <class F>
83 /// An optional function includes a void pointer to self, the arguments of
84 /// @p F, and an additional reference to the VTable, so that it can be
85 /// implemented in terms of other functions.
86 template <class F, class VTable = BasicVTable>
88
89 /// Copy-construct a new instance into storage.
90 required_function_t<void(void *storage) const> copy = nullptr;
91 /// Move-construct a new instance into storage.
92 required_function_t<void(void *storage)> move = nullptr;
93 /// Destruct the given instance.
95 /// The original type of the stored object.
96 const std::type_info *type = &typeid(void);
97
98 BasicVTable() = default;
99
100 template <class T>
101 BasicVTable(std::in_place_t, T &) noexcept {
102 copy = [](const void *self, void *storage) {
103 new (storage) T(*std::launder(reinterpret_cast<const T *>(self)));
104 };
105 // TODO: require that move constructor is noexcept?
106 move = [](void *self, void *storage) noexcept {
107 if constexpr (std::is_const_v<T>)
108 std::terminate();
109 else
110 new (storage)
111 T(std::move(*std::launder(reinterpret_cast<T *>(self))));
112 };
113 destroy = [](void *self) {
114 if constexpr (std::is_const_v<T>)
115 std::terminate();
116 else
117 std::launder(reinterpret_cast<T *>(self))->~T();
118 };
119 type = &typeid(T);
120 }
121};
122
123namespace detail {
124template <class Class, class... ExtraArgs>
125struct Launderer {
126 private:
127 template <auto M, class V, class C, class R, class... Args>
128 [[gnu::always_inline]] static constexpr auto
129 do_invoke(V *self, Args... args, ExtraArgs...) -> R {
130 return std::invoke(M, *std::launder(reinterpret_cast<C *>(self)),
131 std::forward<Args>(args)...);
132 }
133 template <auto M, class T, class R, class... Args>
134 requires std::is_base_of_v<T, Class>
135 [[gnu::always_inline]] static constexpr auto invoker_ovl(R (T::*)(Args...)
136 const) {
137 return do_invoke<M, const void, const Class, R, Args...>;
138 }
139 template <auto M, class T, class R, class... Args>
140 requires std::is_base_of_v<T, Class>
141 [[gnu::always_inline]] static constexpr auto
142 invoker_ovl(R (T::*)(Args...)) {
143 if constexpr (std::is_const_v<Class>)
144 return +[](void *, Args..., ExtraArgs...) {
146 };
147 else
148 return do_invoke<M, void, Class, R, Args...>;
149 }
150
151 public:
152 /// Returns a function that accepts a void pointer, casts it to the class
153 /// type of the member function @p Method, launders it, and then invokes
154 /// @p Method with it, passing on the arguments to @p Method. The function
155 /// can also accept additional arguments at the end, of type @p ExtraArgs.
156 template <auto Method>
157 [[gnu::always_inline]] static constexpr auto invoker() {
159 }
160};
161} // namespace detail
162
163/// @copydoc detail::Launderer::invoker
164template <class Class, auto Method, class... ExtraArgs>
165[[gnu::always_inline]] constexpr auto type_erased_wrapped() {
167}
168
169template <class VTable, class Allocator>
170inline constexpr size_t default_te_buffer_size() {
171 struct S {
172 [[no_unique_address]] Allocator allocator;
173 void *self = nullptr;
174 VTable vtable;
175 };
176 const size_t max_size = 128;
177 return max_size - std::min(max_size, sizeof(S));
178}
179
180template <class... Types>
181inline constexpr size_t required_te_buffer_size_for() {
182 constexpr size_t sizes[] = {sizeof(Types)...};
183 return *std::max_element(std::begin(sizes), std::end(sizes));
184}
185
186/// Class for polymorphism through type erasure. Saves the entire vtable, and
187/// uses small buffer optimization.
188///
189/// @todo Decouple allocation/small buffer optimization.
190template <class VTable = BasicVTable,
191 class Allocator = std::allocator<std::byte>,
194 public:
195 static constexpr size_t small_buffer_size = SmallBufferSize;
197
198 private:
199 using allocator_traits = std::allocator_traits<allocator_type>;
200 using buffer_type = std::array<std::byte, small_buffer_size>;
201 [[no_unique_address]] alignas(std::max_align_t) buffer_type small_buffer;
203
204 private:
205 /// True if @p T is not a child class of @ref TypeErased.
206 template <class T>
207 static constexpr auto no_child_of_ours =
208 !std::is_base_of_v<TypeErased, std::remove_cvref_t<T>>;
209
210 protected:
211 static constexpr size_t invalid_size =
212 static_cast<size_t>(0xDEAD'BEEF'DEAD'BEEF);
213 static constexpr size_t mut_ref_size =
214 static_cast<size_t>(0xFFFF'FFFF'FFFF'FFFF);
215 static constexpr size_t const_ref_size =
216 static_cast<size_t>(0xFFFF'FFFF'FFFF'FFFE);
217 [[nodiscard]] static bool size_indicates_ownership(size_t size) {
218 return size != const_ref_size && size != mut_ref_size;
219 }
220 [[nodiscard]] static bool size_indicates_const(size_t size) {
221 return size == const_ref_size;
222 }
223
224 /// Pointer to the stored object.
225 void *self = nullptr;
226 /// Size required to store the object.
229
230 public:
231 /// Default constructor.
234 /// Default constructor (allocator aware).
237
238 /// Copy constructor.
245 /// Copy constructor (allocator aware).
250 /// Copy constructor (allocator aware).
251 TypeErased(std::allocator_arg_t, const allocator_type &alloc,
252 const TypeErased &other)
253 : TypeErased{other, alloc} {}
254
255 /// Copy assignment.
257 // Check for self-assignment
258 if (&other == this)
259 return *this;
260 // Delete our own storage before assigning a new value
261 cleanup();
262 vtable = other.vtable;
264 return *this;
265 }
266
267 /// Move constructor.
269 : allocator{std::move(other.allocator)},
270 vtable{std::move(other.vtable)} {
271 size = other.size;
272 // If not owned, or if dynamically allocated, simply steal storage,
273 // simply move the pointer.
274 // TODO: is it safe to assume that we can simply move the pointer
275 // without performing a move if we moved the allocator? What if the
276 // allocator has a small buffer?
277 if (!other.owns_referenced_object() || size > small_buffer_size) {
278 // We stole the allocator, so we can steal the storage as well
279 self = std::exchange(other.self, nullptr);
280 }
281 // Otherwise, use the small buffer and do an explicit move
282 else if (other.self) {
283 self = small_buffer.data();
284 vtable.move(other.self, self); // assumed not to throw
285 vtable.destroy(other.self); // nothing to deallocate
286 other.self = nullptr;
287 }
288 other.size = invalid_size;
289 }
290 /// Move constructor (allocator aware).
292 : allocator{alloc}, vtable{std::move(other.vtable)} {
293 // Only continue if other actually contains a value
294 if (other.self == nullptr)
295 return;
296 size = other.size;
297 // If not owned, simply move the pointer
298 if (!other.owns_referenced_object()) {
299 self = std::exchange(other.self, nullptr);
300 }
301 // If dynamically allocated, simply steal other's storage
302 else if (size > small_buffer_size) {
303 // Can we steal the storage because of equal allocators?
304 if (allocator == other.allocator) {
305 self = std::exchange(other.self, nullptr);
306 }
307 // If the allocators are not the same, we cannot steal the
308 // storage, so do an explicit move
309 else {
310 self = allocator.allocate(size);
311 vtable.move(other.self, self);
312 // Cannot call other.cleanup() here because we stole the vtable
313 vtable.destroy(other.self);
314 other.deallocate();
315 }
316 }
317 // Otherwise, use the small buffer and do an explicit move
318 else if (other.self) {
319 self = small_buffer.data();
320 vtable.move(other.self, self);
321 // Cannot call other.cleanup() here because we stole the vtable
322 vtable.destroy(other.self); // nothing to deallocate
323 other.self = nullptr;
324 }
325 other.size = invalid_size;
326 }
327 /// Move constructor (allocator aware).
328 TypeErased(std::allocator_arg_t, const allocator_type &alloc,
330 : TypeErased{std::move(other), alloc} {}
331
332 /// Move assignment.
334 // Check for self-assignment
335 if (&other == this)
336 return *this;
337 // Delete our own storage before assigning a new value
338 cleanup();
339 // Check if we are allowed to steal the allocator
340 static constexpr bool prop_alloc =
341 allocator_traits::propagate_on_container_move_assignment::value;
342 if constexpr (prop_alloc)
343 allocator = std::move(other.allocator);
344 // Only assign if other contains a value
345 if (other.self == nullptr)
346 return *this;
347
348 size = other.size;
349 vtable = std::move(other.vtable);
350 // If not owned, simply move the pointer
351 if (!other.owns_referenced_object()) {
352 self = std::exchange(other.self, nullptr);
353 }
354 // If dynamically allocated, simply steal other's storage
355 else if (size > small_buffer_size) {
356 // Can we steal the storage because of equal allocators?
357 // TODO: is it safe to assume that we can simply move the pointer
358 // without performing a move if we moved the allocator? What if the
359 // allocator has a small buffer?
360 if (prop_alloc || allocator == other.allocator) {
361 self = std::exchange(other.self, nullptr);
362 }
363 // If the allocators are not the same, we cannot steal the
364 // storage, so do an explicit move
365 else {
366 self = allocator.allocate(size);
367 vtable.move(other.self, self); // assumed not to throw
368 vtable.destroy(other.self);
369 // Careful, we might have moved other.allocator!
370 auto &deallocator = prop_alloc ? allocator : other.allocator;
371 using pointer_t = typename allocator_traits::pointer;
372 auto &&other_pointer = static_cast<pointer_t>(other.self);
373 deallocator.deallocate(other_pointer, size);
374 other.self = nullptr;
375 }
376 }
377 // Otherwise, use the small buffer and do an explicit move
378 else if (other.self) {
379 self = small_buffer.data();
380 vtable.move(other.self, self);
381 vtable.destroy(other.self); // nothing to deallocate
382 other.self = nullptr;
383 }
384 other.size = invalid_size;
385 return *this;
386 }
387
388 /// Destructor.
390
391 /// Main constructor that type-erases the given argument.
392 template <class T, class Alloc>
393 requires no_child_of_ours<T>
394 explicit TypeErased(std::allocator_arg_t, const Alloc &alloc, T &&d)
395 : allocator{alloc} {
396 construct_inplace<std::remove_cvref_t<T>>(std::forward<T>(d));
397 }
398 /// Main constructor that type-erases the object constructed from the given
399 /// argument.
400 template <class T, class Alloc, class... Args>
401 explicit TypeErased(std::allocator_arg_t, const Alloc &alloc,
402 std::in_place_type_t<T>, Args &&...args)
403 : allocator{alloc} {
404 construct_inplace<std::remove_cvref_t<T>>(std::forward<Args>(args)...);
405 }
406 /// @copydoc TypeErased(std::allocator_arg_t, const Alloc &, T &&)
407 /// Requirement prevents this constructor from taking precedence over the
408 /// copy and move constructors.
409 template <class T>
410 requires no_child_of_ours<T>
411 explicit TypeErased(T &&d) {
412 construct_inplace<std::remove_cvref_t<T>>(std::forward<T>(d));
413 }
414 /// Main constructor that type-erases the object constructed from the given
415 /// argument.
416 template <class T, class... Args>
417 explicit TypeErased(std::in_place_type_t<T>, Args &&...args) {
418 construct_inplace<std::remove_cvref_t<T>>(std::forward<Args>(args)...);
419 }
420
421 /// Construct a type-erased wrapper of type Ret for an object of type T,
422 /// initialized in-place with the given arguments.
423 template <class Ret, class T, class Alloc, class... Args>
424 requires std::is_base_of_v<TypeErased, Ret>
425 static Ret make(std::allocator_arg_t tag, const Alloc &alloc,
426 Args &&...args) {
427 Ret r{tag, alloc};
428 r.template construct_inplace<T>(std::forward<Args>(args)...);
429 return r;
430 }
431 /// Construct a type-erased wrapper of type Ret for an object of type T,
432 /// initialized in-place with the given arguments.
433 template <class Ret, class T, class... Args>
434 requires no_leading_allocator<Args...>
435 static Ret make(Args &&...args) {
436 return make<Ret, T>(std::allocator_arg, allocator_type{},
437 std::forward<Args>(args)...);
438 }
439
440 /// Check if this wrapper wraps an object. False for default-constructed
441 /// objects.
442 explicit operator bool() const noexcept { return self != nullptr; }
443
444 /// Check if this wrapper owns the storage of the wrapped object, or
445 /// whether it simply stores a reference to an object that was allocated
446 /// elsewhere.
450
451 /// Check if the wrapped object is const.
455
456 /// Get a copy of the allocator.
458
459 /// Query the contained type.
460 [[nodiscard]] const std::type_info &type() const noexcept {
461 return *vtable.type;
462 }
463
464 /// Convert the type-erased object to the given type.
465 /// @throws alpaqa::util::bad_type_erased_type
466 /// If T does not match the stored type.
467 template <class T>
468 requires(!std::is_const_v<T>)
469 [[nodiscard]] T &as() & {
470 if (typeid(T) != type())
471 throw bad_type_erased_type(type(), typeid(T));
474 return *reinterpret_cast<T *>(self);
475 }
476 /// @copydoc as()
477 template <class T>
478 requires(std::is_const_v<T>)
479 [[nodiscard]] T &as() const & {
480 if (typeid(T) != type())
481 throw bad_type_erased_type(type(), typeid(T));
482 return *reinterpret_cast<T *>(self);
483 }
484 /// @copydoc as()
485 template <class T>
486 [[nodiscard]] T &&as() && {
487 if (typeid(T) != type())
488 throw bad_type_erased_type(type(), typeid(T));
489 if (!std::is_const_v<T> && referenced_object_is_const())
491 return std::move(*reinterpret_cast<T *>(self));
492 }
493
494 /// Get a type-erased pointer to the wrapped object.
495 /// @throws alpaqa::util::bad_type_erased_constness
496 /// If the wrapped object is const.
497 /// @see @ref get_const_pointer()
498 [[nodiscard]] void *get_pointer() const {
501 return self;
502 }
503 /// Get a type-erased pointer to the wrapped object.
504 [[nodiscard]] const void *get_const_pointer() const { return self; }
505
506 /// @see @ref derived_from_TypeErased
507 template <std::derived_from<TypeErased> Child>
508 friend void derived_from_TypeErased_helper(const Child &) noexcept {
509 static constexpr bool False = sizeof(Child) != sizeof(Child);
510 static_assert(False, "not allowed in an evaluated context");
511 }
512
513 private:
514 /// Deallocates the storage when destroyed.
526
527 /// Ensure that storage is available, either by using the small buffer if
528 /// it is large enough, or by calling the allocator.
529 /// Returns a RAII wrapper that deallocates the storage unless released.
531 assert(!self);
533 assert(size > 0);
536 : allocator.allocate(size);
537 this->size = size;
538 return {this};
539 }
540
541 /// Deallocate the memory without invoking the destructor.
542 void deallocate() {
544 assert(size > 0);
546 using pointer_t = typename allocator_traits::pointer;
548 allocator.deallocate(reinterpret_cast<pointer_t>(self), size);
549 self = nullptr;
550 }
551
552 /// Destroy the type-erased object (if not empty), and deallocate the memory
553 /// if necessary.
554 void cleanup() {
555 if (!owns_referenced_object()) {
556 self = nullptr;
557 } else if (self) {
558 vtable.destroy(self);
559 deallocate();
560 }
561 }
562
563 template <bool CopyAllocator>
565 constexpr bool prop_alloc =
566 allocator_traits::propagate_on_container_copy_assignment::value;
567 if constexpr (CopyAllocator && prop_alloc)
568 allocator = other.allocator;
569 if (!other)
570 return;
571 if (!other.owns_referenced_object()) {
572 // Non-owning: simply copy the pointer.
573 size = other.size;
574 self = other.self;
575 } else {
576 auto storage_guard = allocate(other.size);
577 // If copy constructor throws, storage should be deallocated and
578 // self set to null, otherwise the TypeErased destructor will
579 // attempt to call the contained object's destructor, which is
580 // undefined behavior if construction failed.
581 vtable.copy(other.self, self);
582 storage_guard.release();
583 }
584 }
585
586 protected:
587 /// Ensure storage and construct the type-erased object of type T in-place.
588 template <class T, class... Args>
590 static_assert(std::is_same_v<T, std::remove_cvref_t<T>>);
591 if constexpr (std::is_pointer_v<T>) {
592 T ptr{args...};
593 using Tnp = std::remove_pointer_t<T>;
594 size = std::is_const_v<Tnp> ? const_ref_size : mut_ref_size;
595 vtable = VTable{std::in_place, *ptr};
596 self = const_cast<std::remove_const_t<Tnp> *>(ptr);
597 } else {
598 // Allocate memory
599 auto storage_guard = allocate(sizeof(T));
600 // Construct the stored object
601 using destroyer = std::unique_ptr<T, noop_delete<T>>;
602#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000
603 // TODO: remove when we drop libc++ 15 support
604 destroyer obj_guard{new (self) T{std::forward<Args>(args)...}};
605#else
606 destroyer obj_guard{std::uninitialized_construct_using_allocator(
607 reinterpret_cast<T *>(self), allocator,
608 std::forward<Args>(args)...)};
609#endif
610 vtable = VTable{std::in_place, static_cast<T &>(*obj_guard.get())};
611 obj_guard.release();
612 storage_guard.release();
613 }
614 }
615
616 /// Call the vtable function @p f with the given arguments @p args,
617 /// implicitly passing the @ref self pointer and @ref vtable reference if
618 /// necessary.
619 template <class Ret, class... FArgs, class... Args>
620 [[gnu::always_inline]] decltype(auto) call(Ret (*f)(const void *, FArgs...),
621 Args &&...args) const {
622 assert(f);
623 assert(self);
624 using LastArg = util::last_type_t<FArgs...>;
625 if constexpr (std::is_same_v<LastArg, const VTable &>)
626 return f(self, std::forward<Args>(args)..., vtable);
627 else
628 return f(self, std::forward<Args>(args)...);
629 }
630 /// @copydoc call
631 template <class Ret, class... FArgs, class... Args>
632 [[gnu::always_inline]] decltype(auto) call(Ret (*f)(void *, FArgs...),
633 Args &&...args) {
634 assert(f);
635 assert(self);
638 using LastArg = util::last_type_t<FArgs...>;
639 if constexpr (std::is_same_v<LastArg, const VTable &>)
640 return f(self, std::forward<Args>(args)..., vtable);
641 else
642 return f(self, std::forward<Args>(args)...);
643 }
644 /// @copydoc call
645 template <class Ret>
646 [[gnu::always_inline]] decltype(auto) call(Ret (*f)(const void *)) const {
647 assert(f);
648 assert(self);
649 return f(self);
650 }
651 /// @copydoc call
652 template <class Ret>
653 [[gnu::always_inline]] decltype(auto) call(Ret (*f)(void *)) {
654 assert(f);
655 assert(self);
658 return f(self);
659 }
660 /// @copydoc call
661 template <class Ret>
662 [[gnu::always_inline]] decltype(auto) call(Ret (*f)(const void *,
663 const VTable &)) const {
664 assert(f);
665 assert(self);
666 return f(self, vtable);
667 }
668 /// @copydoc call
669 template <class Ret>
670 [[gnu::always_inline]] decltype(auto) call(Ret (*f)(void *,
671 const VTable &)) {
672 assert(f);
673 assert(self);
676 return f(self, vtable);
677 }
678};
679
680template <class Child>
682 requires(Child c) { derived_from_TypeErased_helper(c); };
683
684} // namespace alpaqa::util
Class for polymorphism through type erasure.
T & as() &
Convert the type-erased object to the given type.
TypeErased() noexcept(noexcept(allocator_type()) &&noexcept(VTable()))=default
Default constructor.
void * get_pointer() const
Get a type-erased pointer to the wrapped object.
TypeErased(std::allocator_arg_t, const allocator_type &alloc, const TypeErased &other)
Copy constructor (allocator aware).
decltype(auto) call(Ret(*f)(const void *)) const
Call the vtable function f with the given arguments args, implicitly passing the self pointer and vta...
Deallocator allocate(size_t size)
Ensure that storage is available, either by using the small buffer if it is large enough,...
TypeErased & operator=(const TypeErased &other)
Copy assignment.
void construct_inplace(Args &&...args)
Ensure storage and construct the type-erased object of type T in-place.
void deallocate()
Deallocate the memory without invoking the destructor.
TypeErased(std::allocator_arg_t, const Alloc &alloc, T &&d)
Main constructor that type-erases the given argument.
const void * get_const_pointer() const
Get a type-erased pointer to the wrapped object.
static constexpr size_t mut_ref_size
static constexpr size_t small_buffer_size
decltype(auto) call(Ret(*f)(const void *, FArgs...), Args &&...args) const
Call the vtable function f with the given arguments args, implicitly passing the self pointer and vta...
void cleanup()
Destroy the type-erased object (if not empty), and deallocate the memory if necessary.
static constexpr size_t invalid_size
decltype(auto) call(Ret(*f)(void *))
Call the vtable function f with the given arguments args, implicitly passing the self pointer and vta...
TypeErased(std::allocator_arg_t, const Alloc &alloc, std::in_place_type_t< T >, Args &&...args)
Main constructor that type-erases the object constructed from the given argument.
friend void derived_from_TypeErased_helper(const Child &) noexcept
TypeErased(std::in_place_type_t< T >, Args &&...args)
Main constructor that type-erases the object constructed from the given argument.
static Ret make(Args &&...args)
Construct a type-erased wrapper of type Ret for an object of type T, initialized in-place with the gi...
static constexpr size_t const_ref_size
static constexpr auto no_child_of_ours
True if T is not a child class of TypeErased.
TypeErased(const TypeErased &other)
Copy constructor.
TypeErased(TypeErased &&other) noexcept
Move constructor.
bool referenced_object_is_const() const noexcept
Check if the wrapped object is const.
bool owns_referenced_object() const noexcept
Check if this wrapper owns the storage of the wrapped object, or whether it simply stores a reference...
decltype(auto) call(Ret(*f)(void *, FArgs...), Args &&...args)
Call the vtable function f with the given arguments args, implicitly passing the self pointer and vta...
decltype(auto) call(Ret(*f)(const void *, const VTable &)) const
Call the vtable function f with the given arguments args, implicitly passing the self pointer and vta...
size_t size
Size required to store the object.
static bool size_indicates_const(size_t size)
decltype(auto) call(Ret(*f)(void *, const VTable &))
Call the vtable function f with the given arguments args, implicitly passing the self pointer and vta...
T && as() &&
Convert the type-erased object to the given type.
void do_copy_assign(const TypeErased &other)
std::allocator_traits< allocator_type > allocator_traits
TypeErased(std::allocator_arg_t, const allocator_type &alloc, TypeErased &&other) noexcept
Move constructor (allocator aware).
std::array< std::byte, small_buffer_size > buffer_type
TypeErased & operator=(TypeErased &&other) noexcept
Move assignment.
static bool size_indicates_ownership(size_t size)
allocator_type get_allocator() const noexcept
Get a copy of the allocator.
void * self
Pointer to the stored object.
static Ret make(std::allocator_arg_t tag, const Alloc &alloc, Args &&...args)
Construct a type-erased wrapper of type Ret for an object of type T, initialized in-place with the gi...
TypeErased(T &&d)
Main constructor that type-erases the given argument.
TypeErased(TypeErased &&other, const allocator_type &alloc) noexcept
Move constructor (allocator aware).
const std::type_info & type() const noexcept
Query the contained type.
TypeErased(const TypeErased &other, const allocator_type &alloc)
Copy constructor (allocator aware).
T & as() const &
Convert the type-erased object to the given type.
const std::type_info & actual_type
const std::type_info & requested_type
bad_type_erased_type(const std::type_info &actual_type, const std::type_info &requested_type, const std::string &message="")
const char * what() const noexcept override
std::string demangled_typename(const std::type_info &t)
Get the pretty name of the given type as a string.
constexpr size_t required_te_buffer_size_for()
constexpr auto type_erased_wrapped()
Returns a function that accepts a void pointer, casts it to the class type of the member function Met...
typename last_type< Pack... >::type last_type_t
constexpr size_t default_te_buffer_size()
constexpr const auto inf
Definition config.hpp:112
Struct containing function pointers to all problem functions (like the objective and constraint funct...
Struct that stores the size of a polymorphic object, as well as pointers to functions to copy,...
BasicVTable(std::in_place_t, T &) noexcept
required_function_t< void(void *storage) const > copy
Copy-construct a new instance into storage.
typename optional_function< F, VTable >::type optional_function_t
An optional function includes a void pointer to self, the arguments of F, and an additional reference...
const std::type_info * type
The original type of the stored object.
typename required_function< F >::type required_function_t
A required function includes a void pointer to self, in addition to the arguments of F.
required_function_t< void()> destroy
Destruct the given instance.
required_function_t< void(void *storage)> move
Move-construct a new instance into storage.
Deallocates the storage when destroyed.
Deallocator & operator=(const Deallocator &)=delete
Deallocator(const Deallocator &)=delete
Deallocator & operator=(Deallocator &&) noexcept=delete
Deallocator(Deallocator &&o) noexcept
Deallocator(TypeErased *instance) noexcept
static constexpr auto do_invoke(V *self, Args... args, ExtraArgs...) -> R
static constexpr auto invoker()
Returns a function that accepts a void pointer, casts it to the class type of the member function Met...
static constexpr auto invoker_ovl(R(T::*)(Args...))
static constexpr auto invoker_ovl(R(T::*)(Args...) const)