8#include <guanaqo/export.h>
32 const std::string &
message =
"")
36 [[nodiscard]]
const char *
what() const noexcept
override {
38 if (
const char *w = std::logic_error::what(); w && *w) {
57 : std::logic_error{
"Non-const method called on a TypeErased object "
58 "that references a const object"} {}
65template <
class R,
class... Args>
67 using type = R (*)(
void *self, Args...);
69template <
class R,
class... Args>
71 using type = R (*)(
const void *self, Args...);
73template <
class,
class VTable>
75template <
class R,
class... Args,
class VTable>
77 using type = R (*)(
void *self, Args...,
const VTable &);
79template <
class R,
class... Args,
class VTable>
81 using type = R (*)(
const void *self, Args...,
const VTable &);
96template <
class F,
class VTable>
111 [](
const void *self,
void *storage) {
112 new (storage) T(*std::launder(
reinterpret_cast<const T *
>(self)));
115 [](
void *self,
void *storage)
noexcept {
117 if constexpr (std::is_const_v<T>)
121 T(std::move(*std::launder(
reinterpret_cast<T *
>(self))));
125 if constexpr (std::is_const_v<T>)
128 std::destroy_at(std::launder(
reinterpret_cast<T *
>(self)));
137 const std::type_info *
type = &
typeid(void);
149 void copy(
const void *self,
void *storage)
const {
152 void move(
void *self,
void *storage)
const {
163template <
class Class,
class... ExtraArgs>
166 template <
auto M,
class V,
class C,
class R,
class... Args>
167 [[gnu::always_inline]]
static constexpr auto
169 return std::invoke(M, *std::launder(
reinterpret_cast<C *
>(self)),
170 std::forward<Args>(args)...);
172 template <
auto M,
class T,
class R,
class... Args>
173 requires std::is_base_of_v<T, Class>
174 [[gnu::always_inline]]
static constexpr auto invoker_ovl(R (T::*)(Args...)
176 return do_invoke<M,
const void,
const Class, R, Args...>;
178 template <
auto M,
class T,
class R,
class... Args>
179 requires std::is_base_of_v<T, Class>
180 [[gnu::always_inline]]
static constexpr auto
182 if constexpr (std::is_const_v<Class>)
183 return +[](
void *, Args..., ExtraArgs...) {
187 return do_invoke<M, void, Class, R, Args...>;
195 template <auto Method>
196 [[gnu::always_inline]]
static constexpr auto invoker() {
206template <
class Class,
auto Method,
class... ExtraArgs>
211template <
class VTable,
class Allocator>
214 [[no_unique_address]] Allocator allocator;
215 void *self =
nullptr;
218 const size_t max_size = 128;
219 return max_size - std::min(max_size,
sizeof(S));
222template <
class... Types>
224 constexpr size_t sizes[] = {
sizeof(Types)...};
225 return *std::max_element(std::begin(sizes), std::end(sizes));
232template <
class VTable = BasicVTable,
233 class Allocator = std::allocator<std::byte>,
250 !std::is_base_of_v<TypeErased, std::remove_cvref_t<T>>;
254 static_cast<size_t>(0xDEAD'BEEF'DEAD'BEEF);
256 static_cast<size_t>(0xFFFF'FFFF'FFFF'FFFF);
258 static_cast<size_t>(0xFFFF'FFFF'FFFF'FFFE);
275 noexcept(VTable())) = default;
277 template <class Alloc>
312 vtable{std::move(other.vtable)} {
321 self = std::exchange(other.self,
nullptr);
324 else if (other.self) {
327 vtable.destroy(other.self);
328 other.self =
nullptr;
336 if (other.self ==
nullptr)
340 if (!other.owns_referenced_object()) {
341 self = std::exchange(other.self,
nullptr);
347 self = std::exchange(other.self,
nullptr);
355 vtable.destroy(other.self);
360 else if (other.self) {
364 vtable.destroy(other.self);
365 other.self =
nullptr;
382 static constexpr bool prop_alloc =
383 allocator_traits::propagate_on_container_move_assignment::value;
384 if constexpr (prop_alloc)
387 if (other.self ==
nullptr)
391 vtable = std::move(other.vtable);
393 if (!other.owns_referenced_object()) {
394 self = std::exchange(other.self,
nullptr);
402 if (prop_alloc ||
allocator == other.allocator) {
403 self = std::exchange(other.self,
nullptr);
410 vtable.destroy(other.self);
412 auto &deallocator = prop_alloc ?
allocator : other.allocator;
413 using pointer_t =
typename allocator_traits::pointer;
414 auto &&other_pointer =
static_cast<pointer_t
>(other.self);
415 deallocator.deallocate(other_pointer,
size);
416 other.self =
nullptr;
420 else if (other.self) {
423 vtable.destroy(other.self);
424 other.self =
nullptr;
434 template <
class T,
class Alloc>
436 explicit TypeErased(std::allocator_arg_t,
const Alloc &alloc, T &&d)
442 template <
class T,
class Alloc,
class... Args>
443 explicit TypeErased(std::allocator_arg_t,
const Alloc &alloc,
444 std::in_place_type_t<T>, Args &&...args)
458 template <
class T,
class... Args>
459 explicit TypeErased(std::in_place_type_t<T>, Args &&...args) {
465 template <
class Ret,
class T,
class Alloc,
class... Args>
466 requires std::is_base_of_v<TypeErased, Ret>
467 static Ret
make(std::allocator_arg_t tag,
const Alloc &alloc,
475 template <
class Ret,
class T,
class... Args>
477 static Ret
make(Args &&...args) {
479 std::forward<Args>(args)...);
484 explicit operator bool() const noexcept {
return self !=
nullptr; }
502 [[nodiscard]]
const std::type_info &
type() const noexcept {
510 requires(!std::is_const_v<T>)
511 [[nodiscard]] T &
as() & {
512 if (
typeid(T) !=
type())
516 return *
reinterpret_cast<T *
>(
self);
520 requires(std::is_const_v<T>)
521 [[nodiscard]] T &
as()
const & {
522 if (
typeid(T) !=
type())
524 return *
reinterpret_cast<T *
>(
self);
528 [[nodiscard]] T &&
as() && {
529 if (
typeid(T) !=
type())
533 return std::move(*
reinterpret_cast<T *
>(
self));
549 template <std::derived_from<TypeErased> Child>
551 static constexpr bool False =
sizeof(Child) !=
sizeof(Child);
552 static_assert(False,
"not allowed in an evaluated context");
563 :
instance{std::exchange(o.instance,
nullptr)} {}
588 using pointer_t =
typename allocator_traits::pointer;
605 template <
bool CopyAllocator>
607 constexpr bool prop_alloc =
608 allocator_traits::propagate_on_container_copy_assignment::value;
609 if constexpr (CopyAllocator && prop_alloc)
624 storage_guard.release();
630 template <
class T,
class... Args>
632 static_assert(std::is_same_v<T, std::remove_cvref_t<T>>);
633 if constexpr (std::is_pointer_v<T>) {
635 using Tnp = std::remove_pointer_t<T>;
637 vtable = VTable{std::in_place, *ptr};
638 self =
const_cast<std::remove_const_t<Tnp> *
>(ptr);
641 auto storage_guard =
allocate(
sizeof(T));
643 using destroyer = std::unique_ptr<T, noop_delete<T>>;
644#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000
646 destroyer obj_guard{
new (
self) T{std::forward<Args>(args)...}};
648 destroyer obj_guard{std::uninitialized_construct_using_allocator(
650 std::forward<Args>(args)...)};
652 vtable = VTable{std::in_place,
static_cast<T &
>(*obj_guard.get())};
654 storage_guard.release();
661 template <
class Ret,
class... FArgs,
class... Args>
662 [[gnu::always_inline]]
decltype(
auto)
call(Ret (*f)(
const void *, FArgs...),
663 Args &&...args)
const {
667 if constexpr (std::is_same_v<LastArg, const VTable &>)
668 return f(
self, std::forward<Args>(args)...,
vtable);
670 return f(
self, std::forward<Args>(args)...);
673 template <
class Ret,
class... FArgs,
class... Args>
674 [[gnu::always_inline]]
decltype(
auto)
call(Ret (*f)(
void *, FArgs...),
681 if constexpr (std::is_same_v<LastArg, const VTable &>)
682 return f(
self, std::forward<Args>(args)...,
vtable);
684 return f(
self, std::forward<Args>(args)...);
688 [[gnu::always_inline]]
decltype(
auto)
call(Ret (*f)(
const void *))
const {
695 [[gnu::always_inline]]
decltype(
auto)
call(Ret (*f)(
void *)) {
704 [[gnu::always_inline]]
decltype(
auto)
call(Ret (*f)(
const void *,
705 const VTable &))
const {
712 [[gnu::always_inline]]
decltype(
auto)
call(Ret (*f)(
void *,
722template <
class Child>
724 requires(Child c) { derived_from_TypeErased_helper(c); };
static constexpr size_t mut_ref_size
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 small_buffer_size
std::array< std::byte, small_buffer_size > buffer_type
T & as() const &
Convert the type-erased object to the given type.
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.
std::allocator_traits< allocator_type > allocator_traits
TypeErased() noexcept(noexcept(allocator_type()) &&noexcept(VTable()))=default
Default constructor.
TypeErased(const TypeErased &other)
Copy constructor.
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...
const void * get_const_pointer() const
Get a type-erased pointer to the wrapped object.
void construct_inplace(Args &&...args)
Ensure storage and construct the type-erased object of type T in-place.
TypeErased(TypeErased &&other, const allocator_type &alloc) noexcept
Move constructor (allocator aware).
friend void derived_from_TypeErased_helper(const Child &) noexcept
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...
void deallocate()
Deallocate the memory without invoking the destructor.
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...
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...
static constexpr auto no_child_of_ours
True if T is not a child class of TypeErased.
T & as() &
Convert the type-erased object to the given type.
TypeErased(T &&d)
Main constructor that type-erases the given argument.
static bool size_indicates_const(size_t size)
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.
T && as() &&
Convert the type-erased object to the given type.
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...
TypeErased(TypeErased &&other) noexcept
Move constructor.
bool referenced_object_is_const() const noexcept
Check if the wrapped object is const.
void do_copy_assign(const TypeErased &other)
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 allocator_type &alloc, const TypeErased &other)
Copy constructor (allocator aware).
void * self
Pointer to the stored object.
TypeErased(std::allocator_arg_t, const Alloc &alloc, T &&d)
Main constructor that type-erases the given argument.
TypeErased(std::in_place_type_t< T >, Args &&...args)
Main constructor that type-erases the object constructed from the given argument.
void * get_pointer() const
Get a type-erased pointer to the wrapped object.
allocator_type get_allocator() const noexcept
Get a copy of the allocator.
static bool size_indicates_ownership(size_t size)
static constexpr size_t const_ref_size
size_t size
Size required to store the object.
bool owns_referenced_object() const noexcept
Check if this wrapper owns the storage of the wrapped object, or whether it simply stores a reference...
static constexpr size_t invalid_size
void cleanup()
Destroy the type-erased object (if not empty), and deallocate the memory if necessary.
TypeErased(const TypeErased &other, const allocator_type &alloc)
Copy constructor (allocator aware).
TypeErased(std::allocator_arg_t, const allocator_type &alloc, TypeErased &&other) noexcept
Move 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...
const std::type_info & type() const noexcept
Query the contained type.
TypeErased & operator=(TypeErased &&other) noexcept
Move assignment.
bad_type_erased_constness()
const std::type_info & actual_type
bad_type_erased_type(const std::type_info &actual_type, const std::type_info &requested_type, const std::string &message="")
const std::type_info & requested_type
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.
typename last_type< Pack... >::type last_type_t
required_function_t< void(void *storage)> move
Move-construct a new instance into storage.
required_function_t< void(void *storage) const > copy
Copy-construct a new instance into storage.
required_function_t< void()> destroy
Destruct the given instance.
typename detail::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...
constexpr size_t default_te_buffer_size()
typename detail::required_function< F >::type required_function_t
A required function includes a void pointer to self, in addition to the arguments of F.
constexpr size_t required_te_buffer_size_for()
constexpr CopyMoveDestroyVTable copy_move_destroy_vtable
constexpr auto type_erased_wrapped()
Returns a function that accepts a void pointer, casts it to the class type of the member function Met...
Deleter that skips deallocation.
const CopyMoveDestroyVTable * copy_move_destroy_functions
Function pointers for copying, moving and destroying the stored object.
void destroy(void *self) const
const std::type_info * type
The original type of the stored object.
void copy(const void *self, void *storage) const
BasicVTable(std::in_place_t, T &) noexcept
void move(void *self, void *storage) const
Deallocates the storage when destroyed.
Deallocator(const Deallocator &)=delete
Deallocator(Deallocator &&o) noexcept
Deallocator & operator=(Deallocator &&) noexcept=delete
Deallocator(TypeErased *instance) noexcept
Deallocator & operator=(const Deallocator &)=delete
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 do_invoke(V *self, Args... args, ExtraArgs...) -> R
static constexpr auto invoker_ovl(R(T::*)(Args...) const)
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
Lightweight type-trait helpers.