29 const std::type_info &requested_type,
30 const std::string &message =
"")
31 : std::logic_error{message}, actual_type{actual_type},
32 requested_type{requested_type} {}
34 [[nodiscard]]
const char *
what() const noexcept
override {
36 if (
const char *w = std::logic_error::what(); w && *w) {
42 return message.c_str();
58 template <
class R,
class... Args>
60 using type = R (*)(
void *self, Args...);
64 template <
class R,
class... Args>
66 using type = R (*)(
const void *self, Args...);
68 template <
class,
class VTable = BasicVTable>
70 template <
class R,
class... Args,
class VTable>
72 using type = R (*)(
void *self, Args...,
const VTable &);
74 template <
class,
class VTable = BasicVTable>
76 template <
class R,
class... Args,
class VTable>
78 using type = R (*)(
const void *self, Args...,
const VTable &);
91 template <
class F,
class VTable = BasicVTable>
95 template <
class F,
class VTable = BasicVTable>
106 const std::type_info *
type = &
typeid(void);
112 copy = [](
const void *self,
void *storage) {
113 new (storage) T(*std::launder(
reinterpret_cast<const T *
>(self)));
116 move = [](
void *self,
void *storage)
noexcept {
118 T(std::move(*std::launder(
reinterpret_cast<T *
>(self))));
121 std::launder(
reinterpret_cast<T *
>(self))->~T();
128template <
class... ExtraArgs>
131 template <
auto M,
class V,
class T,
class R,
class... Args>
132 [[gnu::always_inline]]
static constexpr auto
134 return std::invoke(M, *std::launder(
reinterpret_cast<T *
>(self)),
135 std::forward<Args>(args)...);
137 template <
auto M,
class T,
class R,
class... Args>
138 [[gnu::always_inline]]
static constexpr auto invoker_ovl(R (T::*)(Args...)
140 return do_invoke<M,
const void,
const T, R, Args...>;
142 template <
auto M,
class T,
class R,
class... Args>
143 [[gnu::always_inline]]
static constexpr auto
145 return do_invoke<M, void, T, R, Args...>;
153 template <auto Method>
154 [[gnu::always_inline]]
static constexpr auto invoker() {
155 return invoker_ovl<Method>(Method);
161template <
auto Method,
class... ExtraArgs>
166template <
class VTable,
class Allocator>
169 [[no_unique_address]] Allocator allocator;
170 void *self =
nullptr;
173 const size_t max_size = 128;
174 return max_size - std::min(max_size,
sizeof(S));
177template <
class... Types>
179 constexpr size_t sizes[] = {
sizeof(Types)...};
180 return *std::max_element(std::begin(sizes), std::end(sizes));
195 class Allocator = std::allocator<std::byte>,
196 size_t SmallBufferSize = default_te_buffer_size<VTable, Allocator>()>
212 !std::is_base_of_v<TypeErased, std::remove_cvref_t<T>>;
216 static_cast<size_t>(0xDEADBEEFDEADBEEF);
227 template <class Alloc>
233 do_copy_assign<false>(other);
238 do_copy_assign<false>(other);
247 do_copy_assign<true>(other);
253 :
allocator{std::move(other.allocator)} {
259 self = std::exchange(other.self,
nullptr);
262 else if (other.self) {
265 vtable.destroy(other.self);
266 other.self =
nullptr;
273 if (other.self ==
nullptr)
276 vtable = std::move(other.vtable);
281 self = std::exchange(other.self,
nullptr);
289 vtable.destroy(other.self);
294 else if (other.self) {
298 vtable.destroy(other.self);
299 other.self =
nullptr;
310 static constexpr bool prop_alloc =
311 allocator_traits::propagate_on_container_move_assignment::value;
312 if constexpr (prop_alloc)
315 if (other.self ==
nullptr)
319 vtable = std::move(other.vtable);
323 if (prop_alloc ||
allocator == other.allocator) {
324 self = std::exchange(other.self,
nullptr);
331 vtable.destroy(other.self);
333 auto &deallocator = prop_alloc ?
allocator : other.allocator;
334 using pointer_t =
typename allocator_traits::pointer;
335 auto &&other_pointer =
static_cast<pointer_t
>(other.self);
336 deallocator.deallocate(other_pointer,
size);
337 other.self =
nullptr;
341 else if (other.self) {
344 vtable.destroy(other.self);
345 other.self =
nullptr;
354 template <
class T,
class Alloc>
355 explicit TypeErased(std::allocator_arg_t,
const Alloc &alloc, T &&d)
357 construct_inplace<std::remove_cvref_t<T>>(std::forward<T>(d));
361 template <
class T,
class Alloc,
class... Args>
362 explicit TypeErased(std::allocator_arg_t,
const Alloc &alloc,
365 construct_inplace<std::remove_cvref_t<T>>(std::forward<Args>(args)...);
371 requires no_child_of_ours<T>
373 construct_inplace<std::remove_cvref_t<T>>(std::forward<T>(d));
377 template <
class T,
class... Args>
379 construct_inplace<std::remove_cvref_t<T>>(std::forward<Args>(args)...);
384 template <
class Ret,
class T,
class Alloc,
class... Args>
385 requires std::is_base_of_v<TypeErased, Ret>
386 static Ret
make(std::allocator_arg_t tag,
const Alloc &alloc,
389 r.template construct_inplace<T>(std::forward<Args>(args)...);
394 template <
class Ret,
class T,
class... Args>
396 static Ret
make(Args &&...args) {
398 std::forward<Args>(args)...);
403 explicit operator bool() const noexcept {
return self !=
nullptr; }
409 [[nodiscard]]
const std::type_info &
type() const noexcept {
417 if (
typeid(T) !=
type())
419 return *
reinterpret_cast<T *
>(
self);
423 const T &
as() const & {
424 if (
typeid(T) !=
type())
426 return *
reinterpret_cast<const T *
>(
self);
431 if (
typeid(T) !=
type())
433 return std::move(*
reinterpret_cast<T *
>(
self));
444 :
instance{std::exchange(o.instance,
nullptr)} {}
465 using pointer_t =
typename allocator_traits::pointer;
480 template <
bool CopyAllocator>
482 constexpr bool prop_alloc =
483 allocator_traits::propagate_on_container_copy_assignment::value;
484 if constexpr (CopyAllocator && prop_alloc)
495 storage_guard.release();
500 template <
class T,
class... Args>
502 static_assert(std::is_same_v<T, std::remove_cvref_t<T>>);
504 auto storage_guard =
allocate(
sizeof(T));
506 using destroyer = std::unique_ptr<T, noop_delete<T>>;
507 destroyer object_guard{
new (
self) T{std::forward<Args>(args)...}};
509 object_guard.release();
510 storage_guard.release();
516 template <
class Ret,
class... FArgs,
class... Args>
517 [[gnu::always_inline]]
decltype(
auto)
call(Ret (*f)(
const void *, FArgs...),
518 Args &&...args)
const {
522 if constexpr (std::is_same_v<LastArg, const VTable &>)
523 return f(
self, std::forward<Args>(args)...,
vtable);
525 return f(
self, std::forward<Args>(args)...);
528 template <
class Ret,
class... FArgs,
class... Args>
529 [[gnu::always_inline]]
decltype(
auto)
call(Ret (*f)(
void *, FArgs...),
534 if constexpr (std::is_same_v<LastArg, const VTable &>)
535 return f(
self, std::forward<Args>(args)...,
vtable);
537 return f(
self, std::forward<Args>(args)...);
541 [[gnu::always_inline]]
decltype(
auto)
call(Ret (*f)(
const void *))
const {
548 [[gnu::always_inline]]
decltype(
auto)
call(Ret (*f)(
void *)) {
555 [[gnu::always_inline]]
decltype(
auto)
call(Ret (*f)(
const void *,
556 const VTable &))
const {
563 [[gnu::always_inline]]
decltype(
auto)
call(Ret (*f)(
void *,
Class for polymorphism through type erasure.
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.
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...
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 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.
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.
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.
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()
typename last_type< Pack... >::type last_type_t
constexpr auto type_erased_wrapped()
Returns a function that accepts a void pointer, casts it to the class type of the member function Met...
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.
R(*)(const void *self, Args..., const VTable &) type
R(*)(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,...
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.
BasicVTable(VTableTypeTag< T >) noexcept
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_ovl(R(T::*)(Args...) const)
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...))