16 template <DerivedType Type>
18 void (*move_to)(storage&, storage&)
noexcept;
19 void (*destroy)(storage&)
noexcept;
20 Return (*invoke)(storage
const&, Xs&&...)
noexcept(Noexcept);
24 struct vtable_base<DerivedType::Copyable> : vtable_base<DerivedType::MoveOnly> {
25 void (*copy_to)(storage
const&, storage&);
28 struct vtable : vtable_base<Type> {};
31 static constexpr size_t embedded_target_offset =
32 alignof(Fn) <=
sizeof(vtable*) ? 0 : (
alignof(Fn) -
sizeof(vtable*));
35 static constexpr size_t embedded_target_available_size = embedded_target_size - embedded_target_offset<Fn>;
38 static constexpr bool is_heap_target =
alignof(Fn) >
alignof(std::max_align_t)
39 ||
sizeof(Fn) > embedded_target_available_size<Fn>
40 || !std::is_nothrow_move_constructible_v<Fn>;
47 union alignas(max_align_t) {
49 char embedded_target[embedded_target_size];
54 [[nodiscard]]
void* embedded_target_ptr()
noexcept {
55 return &embedded_target + embedded_target_offset<Fn>;
59 [[nodiscard]] Fn* small_fn_ptr()
const noexcept {
60 return static_cast<Fn*
>(
const_cast<storage*
>(
this)->embedded_target_ptr<Fn>());
64 [[nodiscard]] Fn* large_fn_ptr()
const noexcept {
65 return static_cast<Fn*
>(heap_target);
68 void set_large_fn_ptr(
void*
const v)
noexcept { heap_target = v; }
70 template <
class Fn,
class FnInvQuals,
bool HeapTarget>
72 static Fn* target(storage
const& self) {
73 if constexpr (HeapTarget) {
74 return self.large_fn_ptr<Fn>();
76 return self.small_fn_ptr<Fn>();
80 static void copy_to(storage
const& self, storage& to) {
81 if constexpr (Type == DerivedType::Copyable) {
82 if constexpr (HeapTarget) {
83 to.set_large_fn_ptr(::new Fn(*target(self)));
85 ::new (to.embedded_target_ptr<Fn>()) Fn(*target(self));
90 static void move_to(storage& self, storage& to)
noexcept {
91 if constexpr (HeapTarget) {
92 to.set_large_fn_ptr(target(self));
93 self.set_large_fn_ptr(
nullptr);
95 auto const ptr = target(self);
96 ::new (to.embedded_target_ptr<Fn>()) Fn(std::move(*ptr));
101 static void destroy(storage& self)
noexcept {
102 if constexpr (HeapTarget) {
109 static Return invoke(storage
const& self, Xs&&... args)
noexcept(Noexcept) {
110 if constexpr (std::is_void_v<Return>) {
111 (void)std::invoke(
static_cast<FnInvQuals
>(*target(self)), std::forward<Xs>(args)...);
113 return std::invoke(
static_cast<FnInvQuals
>(*target(self)), std::forward<Xs>(args)...);
118 template <
class Fn,
class FnInvQuals>
119 [[nodiscard]]
static constexpr vtable create_vtable()
noexcept {
122 using impl_type = vtable_impl<Fn, FnInvQuals, is_heap_target<Fn>>;
123 impl.move_to = &impl_type::move_to;
124 impl.destroy = &impl_type::destroy;
125 impl.invoke = &impl_type::invoke;
126 if constexpr (Type == DerivedType::Copyable) {
127 impl.copy_to = &impl_type::copy_to;
132 template <
class Fn,
class FnInvQuals>
133 static constexpr vtable vfstorage = create_vtable<Fn, FnInvQuals>();
138 vtable
const& get_vtable()
const noexcept {
return *mStorage.vfptr; }
142 void construct_empty() { mStorage.vfptr =
nullptr; }
143 template <
class Fn,
class FnInvQuals,
class... Ys>
144 void construct_target(Ys&&... args) {
145 mStorage.vfptr = &vfstorage<Fn, FnInvQuals>;
146 if constexpr (is_heap_target<Fn>) {
147 auto ptr = std::make_unique<Fn>(std::forward<Ys>(args)...);
148 mStorage.set_large_fn_ptr(ptr.release());
150 ::new (mStorage.embedded_target_ptr<Fn>()) Fn(std::forward<Ys>(args)...);
154 auto get_invoke()
const noexcept {
158 return get_vtable().invoke;
162 explicit operator bool()
const noexcept {
return mStorage.vfptr !=
nullptr; }
171 this->get_vtable().destroy(this->mStorage);
176 other.get_vtable().move_to(other.mStorage, this->mStorage);
177 this->mStorage.vfptr = std::exchange(other.mStorage.vfptr,
nullptr);
179 this->construct_empty();
183 if (
this != std::addressof(other)) {
185 this->get_vtable().destroy(this->mStorage);
188 other.get_vtable().move_to(other.mStorage, this->mStorage);
189 this->mStorage.vfptr = std::exchange(other.mStorage.vfptr,
nullptr);
191 this->construct_empty();
200 this->get_vtable().destroy(this->mStorage);
201 this->mStorage.vfptr =
nullptr;
206 [[nodiscard]]
friend bool operator==(
function_base const& self, nullptr_t)
noexcept {
207 return !
static_cast<bool>(self);
220 other.get_vtable().copy_to(other.mStorage, this->mStorage);
221 this->mStorage.vfptr = other.mStorage.vfptr;
223 this->construct_empty();
227 if (
this != std::addressof(other)) {
229 this->get_vtable().destroy(this->mStorage);
232 other.get_vtable().copy_to(other.mStorage, this->mStorage);
233 this->mStorage.vfptr = other.mStorage.vfptr;
235 this->construct_empty();
247:
public function_base<Type, function_base_impl<Type, Return, false, Xs...>> {
250 using FnInvQuals = Fn&;
253 static constexpr bool is_callable_from =
254 std::is_invocable_r_v<Return, Fn, Xs...> && std::is_invocable_r_v<Return, Fn&, Xs...>;
257 using result_type = Return;
259 Return operator()(Xs... args) {
return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
296:
public function_base<Type, function_base_impl<Type, Return, false, Xs...>> {
299 using FnInvQuals = Fn
const&;
302 static constexpr bool is_callable_from =
303 std::is_invocable_r_v<Return, Fn
const, Xs...> && std::is_invocable_r_v<Return, Fn
const&, Xs...>;
306 using result_type = Return;
308 Return operator()(Xs... args)
const {
return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
316 using FnInvQuals = Fn
const&;
319 static constexpr bool is_callable_from = std::is_invocable_r_v<Return, Fn
const&, Xs...>;
322 using result_type = Return;
324 Return operator()(Xs... args)
const& {
return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
332 using FnInvQuals = Fn
const&&;
335 static constexpr bool is_callable_from = std::is_invocable_r_v<Return, Fn
const, Xs...>;
338 using result_type = Return;
340 Return operator()(Xs... args)
const&& {
return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
345:
public function_base<Type, function_base_impl<Type, Return, true, Xs...>> {
348 using FnInvQuals = Fn&;
351 static constexpr bool is_callable_from =
352 std::is_nothrow_invocable_r_v<Return, Fn, Xs...> && std::is_nothrow_invocable_r_v<Return, Fn&, Xs...>;
355 using result_type = Return;
357 Return operator()(Xs... args)
noexcept {
return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
394:
public function_base<Type, function_base_impl<Type, Return, true, Xs...>> {
397 using FnInvQuals = Fn
const&;
400 static constexpr bool is_callable_from = std::is_nothrow_invocable_r_v<Return, Fn
const, Xs...>
401 && std::is_nothrow_invocable_r_v<Return, Fn
const&, Xs...>;
404 using result_type = Return;
406 Return operator()(Xs... args)
const noexcept {
407 return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...);
416 using FnInvQuals = Fn
const&;
419 static constexpr bool is_callable_from = std::is_nothrow_invocable_r_v<Return, Fn
const&, Xs...>;
422 using result_type = Return;
424 Return operator()(Xs... args)
const&
noexcept {
425 return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...);
434 using FnInvQuals = Fn
const&&;
437 static constexpr bool is_callable_from = std::is_nothrow_invocable_r_v<Return, Fn
const, Xs...>;
440 using result_type = Return;
442 Return operator()(Xs... args)
const&&
noexcept {
443 return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...);
452 static constexpr bool enable_one_arg_constructor =
454 || std::is_base_of_v<function_invoke, std::remove_cvref_t<Fn>>)
455 && !ll::traits::is_specialization_of_v<std::remove_cvref_t<Fn>, std::in_place_type_t>
456 && base::template is_callable_from<std::decay_t<Fn>>;
458 template <
class Fn,
class... Xs>
459 static constexpr bool enable_in_place_constructor =
460 std::is_constructible_v<std::decay_t<Fn>, Xs...> && base::template is_callable_from<std::decay_t<Fn>>;
462 template <
class Fn,
class U,
class... Xs>
463 static constexpr bool enable_in_place_list_constructor =
464 std::is_constructible_v<std::decay_t<Fn>, std::initializer_list<U>&, Xs...>
465 && base::template is_callable_from<std::decay_t<Fn>>;
471 requires enable_one_arg_constructor<F>
473 using Fn = std::decay_t<F>;
474 static_assert(std::is_constructible_v<Fn, F>);
475 if constexpr (std::is_member_pointer_v<Fn> || std::is_pointer_v<Fn> ||
requires(F e) {
476 { e ==
nullptr } -> std::same_as<bool>;
479 this->construct_empty();
483 using FnInvQuals = base::template FnInvQuals<Fn>;
484 this->
template construct_target<Fn, FnInvQuals>(std::forward<F>(f));
487 template <
class F,
class... Xs>
488 requires enable_in_place_constructor<F, Xs...>
490 using Fn = std::decay_t<F>;
491 static_assert(std::is_same_v<Fn, F>);
492 using FnInvQuals = base::template FnInvQuals<Fn>;
493 this->
template construct_target<Fn, FnInvQuals>(std::forward<Xs>(args)...);
496 template <
class F,
class U,
class... Xs>
497 requires enable_in_place_list_constructor<F, U, Xs...>
498 explicit function_invoke(std::in_place_type_t<F>, std::initializer_list<U> l, Xs&&... args) {
499 using Fn = std::decay_t<F>;
500 static_assert(std::is_same_v<Fn, F>);
501 using FnInvQuals = base::template FnInvQuals<Fn>;
502 this->
template construct_target<Fn, FnInvQuals>(l, std::forward<Xs>(args)...);
508 other = std::move(*
this);
509 *
this = std::move(tmp);