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} {}
30 [[nodiscard]]
const char *
what() const noexcept
override {
32 if (
const char *w = std::logic_error::what(); w && *w) {
38 return message.c_str();
50 : std::logic_error{
"Non-const method called on a TypeErased object "
51 "that references a const object"} {}
61 template <
class R,
class...
Args>
65 template <
class R,
class...
Args>
69 template <
class,
class VTable = BasicVTable>
71 template <
class R,
class...
Args,
class VTable>
73 using type = R (*)(
void *self,
Args...,
const VTable &);
75 template <
class R,
class...
Args,
class VTable>
77 using type = R (*)(
const void *self,
Args...,
const VTable &);
86 template <
class F,
class VTable = BasicVTable>
102 copy = [](
const void *self,
void *storage) {
103 new (storage)
T(*std::launder(
reinterpret_cast<const T *
>(self)));
106 move = [](
void *self,
void *storage)
noexcept {
107 if constexpr (std::is_const_v<T>)
111 T(std::move(*std::launder(
reinterpret_cast<T *
>(self))));
114 if constexpr (std::is_const_v<T>)
117 std::launder(
reinterpret_cast<T *
>(self))->~T();
127 template <
auto M,
class V,
class C,
class R,
class...
Args>
128 [[gnu::always_inline]]
static constexpr auto
130 return std::invoke(
M, *std::launder(
reinterpret_cast<C *
>(self)),
131 std::forward<Args>(
args)...);
133 template <
auto M,
class T,
class R,
class...
Args>
134 requires std::is_base_of_v<T, Class>
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
143 if constexpr (std::is_const_v<Class>)
156 template <auto Method>
157 [[gnu::always_inline]]
static constexpr auto invoker() {
169template <
class VTable,
class Allocator>
172 [[no_unique_address]] Allocator allocator;
173 void *self =
nullptr;
180template <
class...
Types>
182 constexpr size_t sizes[] = {
sizeof(
Types)...};
183 return *std::max_element(std::begin(sizes), std::end(sizes));
190template <
class VTable = BasicVTable,
191 class Allocator = std::allocator<std::byte>,
208 !std::is_base_of_v<TypeErased, std::remove_cvref_t<T>>;
212 static_cast<size_t>(0xDEAD'BEEF'DEAD'BEEF);
214 static_cast<size_t>(0xFFFF'FFFF'FFFF'FFFF);
216 static_cast<size_t>(0xFFFF'FFFF'FFFF'FFFE);
279 self = std::exchange(
other.self,
nullptr);
282 else if (
other.self) {
286 other.self =
nullptr;
294 if (
other.self ==
nullptr)
298 if (!
other.owns_referenced_object()) {
299 self = std::exchange(
other.self,
nullptr);
305 self = std::exchange(
other.self,
nullptr);
318 else if (
other.self) {
323 other.self =
nullptr;
341 allocator_traits::propagate_on_container_move_assignment::value;
345 if (
other.self ==
nullptr)
351 if (!
other.owns_referenced_object()) {
352 self = std::exchange(
other.self,
nullptr);
361 self = std::exchange(
other.self,
nullptr);
371 using pointer_t =
typename allocator_traits::pointer;
374 other.self =
nullptr;
378 else if (
other.self) {
382 other.self =
nullptr;
392 template <
class T,
class Alloc>
400 template <
class T,
class Alloc,
class...
Args>
402 std::in_place_type_t<T>,
Args &&...
args)
416 template <
class T,
class...
Args>
424 requires std::is_base_of_v<TypeErased, Ret>
433 template <
class Ret,
class T,
class...
Args>
437 std::forward<Args>(
args)...);
468 requires(!std::is_const_v<T>)
470 if (
typeid(
T) !=
type())
474 return *
reinterpret_cast<T *
>(
self);
478 requires(std::is_const_v<T>)
480 if (
typeid(
T) !=
type())
482 return *
reinterpret_cast<T *
>(
self);
487 if (
typeid(
T) !=
type())
491 return std::move(*
reinterpret_cast<T *
>(
self));
507 template <std::derived_from<TypeErased> Child>
509 static constexpr bool False =
sizeof(
Child) !=
sizeof(
Child);
510 static_assert(False,
"not allowed in an evaluated context");
521 :
instance{std::exchange(
o.instance,
nullptr)} {}
546 using pointer_t =
typename allocator_traits::pointer;
563 template <
bool CopyAllocator>
566 allocator_traits::propagate_on_container_copy_assignment::value;
571 if (!
other.owns_referenced_object()) {
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>) {
593 using Tnp = std::remove_pointer_t<T>;
595 vtable = VTable{std::in_place, *ptr};
596 self =
const_cast<std::remove_const_t<Tnp> *
>(ptr);
601 using destroyer = std::unique_ptr<T, noop_delete<T>>;
602#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000
608 std::forward<Args>(
args)...)};
620 [[gnu::always_inline]]
decltype(
auto)
call(
Ret (*f)(
const void *,
FArgs...),
621 Args &&...args)
const {
625 if constexpr (std::is_same_v<LastArg, const VTable &>)
628 return f(
self, std::forward<Args>(
args)...);
639 if constexpr (std::is_same_v<LastArg, const VTable &>)
642 return f(
self, std::forward<Args>(
args)...);
646 [[gnu::always_inline]]
decltype(
auto)
call(
Ret (*f)(
const void *))
const {
653 [[gnu::always_inline]]
decltype(
auto)
call(
Ret (*f)(
void *)) {
662 [[gnu::always_inline]]
decltype(
auto)
call(
Ret (*f)(
const void *,
663 const VTable &))
const {
670 [[gnu::always_inline]]
decltype(
auto)
call(
Ret (*f)(
void *,
680template <
class Child>
682 requires(
Child c) { derived_from_TypeErased_helper(c); };
std::allocator< std::byte > allocator_type
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.
bad_type_erased_constness()
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()
Struct containing function pointers to all problem functions (like the objective and constraint funct...
R(*)(void *self, Args..., const VTable &) type
R(*)(const void *self, Args..., const VTable &) type
R(*)(const void *self, Args...) type
R(*)(void *self, Args...) type
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)