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