LeviLamina
Loading...
Searching...
No Matches
function.h
1#pragma once
2
3#include <functional>
4
5#include "ll/api/base/TypeTraits.h"
6#include "mc/platform/brstd/detail/DerivedType.h"
7
8namespace brstd::detail::function {
9
10constexpr size_t embedded_target_size = sizeof(void*) * 8 - sizeof(void*);
11
12template <DerivedType Type, class Return, bool Noexcept, class... Xs>
14 class storage;
15
16 template <DerivedType Type>
17 struct vtable_base {
18 void (*move_to)(storage&, storage&) noexcept;
19 void (*destroy)(storage&) noexcept;
20 Return (*invoke)(storage const&, Xs&&...) noexcept(Noexcept);
21 };
22
23 template <>
24 struct vtable_base<DerivedType::Copyable> : vtable_base<DerivedType::MoveOnly> {
25 void (*copy_to)(storage const&, storage&);
26 };
27
28 struct vtable : vtable_base<Type> {};
29
30 template <class Fn>
31 static constexpr size_t embedded_target_offset =
32 alignof(Fn) <= sizeof(vtable*) ? 0 : (alignof(Fn) - sizeof(vtable*));
33
34 template <class Fn>
35 static constexpr size_t embedded_target_available_size = embedded_target_size - embedded_target_offset<Fn>;
36
37 template <class 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>;
41
42 class storage {
43 public:
44 vtable const* vfptr;
45
46 private:
47 union alignas(max_align_t) {
48 void* heap_target;
49 char embedded_target[embedded_target_size];
50 };
51
52 public:
53 template <class Fn>
54 [[nodiscard]] void* embedded_target_ptr() noexcept {
55 return &embedded_target + embedded_target_offset<Fn>;
56 }
57
58 template <class Fn>
59 [[nodiscard]] Fn* small_fn_ptr() const noexcept {
60 return static_cast<Fn*>(const_cast<storage*>(this)->embedded_target_ptr<Fn>());
61 }
62
63 template <class Fn>
64 [[nodiscard]] Fn* large_fn_ptr() const noexcept {
65 return static_cast<Fn*>(heap_target);
66 }
67
68 void set_large_fn_ptr(void* const v) noexcept { heap_target = v; }
69 };
70 template <class Fn, class FnInvQuals, bool HeapTarget>
71 struct vtable_impl {
72 static Fn* target(storage const& self) {
73 if constexpr (HeapTarget) {
74 return self.large_fn_ptr<Fn>();
75 } else {
76 return self.small_fn_ptr<Fn>();
77 }
78 }
79
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)));
84 } else {
85 ::new (to.embedded_target_ptr<Fn>()) Fn(*target(self));
86 }
87 }
88 }
89
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);
94 } else {
95 auto const ptr = target(self);
96 ::new (to.embedded_target_ptr<Fn>()) Fn(std::move(*ptr));
97 ptr->~Fn();
98 }
99 }
100
101 static void destroy(storage& self) noexcept {
102 if constexpr (HeapTarget) {
103 delete target(self);
104 } else {
105 target(self)->~Fn();
106 }
107 }
108
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)...);
112 } else {
113 return std::invoke(static_cast<FnInvQuals>(*target(self)), std::forward<Xs>(args)...);
114 }
115 }
116 };
117
118 template <class Fn, class FnInvQuals>
119 [[nodiscard]] static constexpr vtable create_vtable() noexcept {
120 vtable impl{};
121
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;
128 }
129 return impl;
130 }
131
132 template <class Fn, class FnInvQuals>
133 static constexpr vtable vfstorage = create_vtable<Fn, FnInvQuals>();
134
135protected:
136 storage mStorage;
137
138 vtable const& get_vtable() const noexcept { return *mStorage.vfptr; }
139
140 function_base_impl() noexcept = default;
141
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());
149 } else {
150 ::new (mStorage.embedded_target_ptr<Fn>()) Fn(std::forward<Ys>(args)...);
151 }
152 }
153
154 auto get_invoke() const noexcept {
155 if (!*this) {
156 std::abort();
157 }
158 return get_vtable().invoke;
159 }
160
161public:
162 explicit operator bool() const noexcept { return mStorage.vfptr != nullptr; }
163};
164
165template <DerivedType, class Base>
166class function_base : public Base {
167protected:
168 function_base() { this->construct_empty(); }
170 if (*this) {
171 this->get_vtable().destroy(this->mStorage);
172 }
173 }
175 if (other) {
176 other.get_vtable().move_to(other.mStorage, this->mStorage);
177 this->mStorage.vfptr = std::exchange(other.mStorage.vfptr, nullptr);
178 } else {
179 this->construct_empty();
180 }
181 }
182 function_base& operator=(function_base&& other) {
183 if (this != std::addressof(other)) {
184 if (*this) {
185 this->get_vtable().destroy(this->mStorage);
186 }
187 if (other) {
188 other.get_vtable().move_to(other.mStorage, this->mStorage);
189 this->mStorage.vfptr = std::exchange(other.mStorage.vfptr, nullptr);
190 } else {
191 this->construct_empty();
192 }
193 }
194 return *this;
195 }
196
197public:
198 function_base& operator=(std::nullptr_t) {
199 if (*this) {
200 this->get_vtable().destroy(this->mStorage);
201 this->mStorage.vfptr = nullptr;
202 }
203 return *this;
204 }
205
206 [[nodiscard]] friend bool operator==(function_base const& self, nullptr_t) noexcept {
207 return !static_cast<bool>(self);
208 }
209};
210
211template <class Base>
212class function_base<DerivedType::Copyable, Base> : public function_base<DerivedType::MoveOnly, Base> {
213protected:
214 function_base() = default;
215 function_base(function_base&&) = default;
216 function_base& operator=(function_base&&) = default;
217
218 function_base(function_base const& other) {
219 if (other) {
220 other.get_vtable().copy_to(other.mStorage, this->mStorage);
221 this->mStorage.vfptr = other.mStorage.vfptr;
222 } else {
223 this->construct_empty();
224 }
225 }
226 function_base& operator=(function_base const& other) {
227 if (this != std::addressof(other)) {
228 if (*this) {
229 this->get_vtable().destroy(this->mStorage);
230 }
231 if (other) {
232 other.get_vtable().copy_to(other.mStorage, this->mStorage);
233 this->mStorage.vfptr = other.mStorage.vfptr;
234 } else {
235 this->construct_empty();
236 }
237 }
238 return *this;
239 }
240};
241
242template <DerivedType Type, class Signature>
244
245template <DerivedType Type, class Return, class... Xs>
246class function_invoke_base<Type, Return(Xs...)>
247: public function_base<Type, function_base_impl<Type, Return, false, Xs...>> {
248public:
249 template <class Fn>
250 using FnInvQuals = Fn&;
251
252 template <class 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...>;
255
256public:
257 using result_type = Return;
258
259 Return operator()(Xs... args) { return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
260};
261
262template <DerivedType Type, class Return, class... Xs>
263class function_invoke_base<Type, Return(Xs...)&>
264: public function_base<Type, function_base_impl<Type, Return, false, Xs...>> {
265public:
266 template <class Fn>
267 using FnInvQuals = Fn&;
268
269 template <class Fn>
270 static constexpr bool is_callable_from = std::is_invocable_r_v<Return, Fn&, Xs...>;
271
272public:
273 using result_type = Return;
274
275 Return operator()(Xs... args) & { return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
276};
277
278template <DerivedType Type, class Return, class... Xs>
279class function_invoke_base<Type, Return(Xs...) &&>
280: public function_base<Type, function_base_impl<Type, Return, false, Xs...>> {
281public:
282 template <class Fn>
283 using FnInvQuals = Fn&&;
284
285 template <class Fn>
286 static constexpr bool is_callable_from = std::is_invocable_r_v<Return, Fn, Xs...>;
287
288public:
289 using result_type = Return;
290
291 Return operator()(Xs... args) && { return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
292};
293
294template <DerivedType Type, class Return, class... Xs>
295class function_invoke_base<Type, Return(Xs...) const>
296: public function_base<Type, function_base_impl<Type, Return, false, Xs...>> {
297public:
298 template <class Fn>
299 using FnInvQuals = Fn const&;
300
301 template <class Fn>
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...>;
304
305public:
306 using result_type = Return;
307
308 Return operator()(Xs... args) const { return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
309};
310
311template <DerivedType Type, class Return, class... Xs>
312class function_invoke_base<Type, Return(Xs...) const&>
313: public function_base<Type, function_base_impl<Type, Return, false, Xs...>> {
314public:
315 template <class Fn>
316 using FnInvQuals = Fn const&;
317
318 template <class Fn>
319 static constexpr bool is_callable_from = std::is_invocable_r_v<Return, Fn const&, Xs...>;
320
321public:
322 using result_type = Return;
323
324 Return operator()(Xs... args) const& { return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
325};
326
327template <DerivedType Type, class Return, class... Xs>
328class function_invoke_base<Type, Return(Xs...) const&&>
329: public function_base<Type, function_base_impl<Type, Return, false, Xs...>> {
330public:
331 template <class Fn>
332 using FnInvQuals = Fn const&&;
333
334 template <class Fn>
335 static constexpr bool is_callable_from = std::is_invocable_r_v<Return, Fn const, Xs...>;
336
337public:
338 using result_type = Return;
339
340 Return operator()(Xs... args) const&& { return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
341};
342
343template <DerivedType Type, class Return, class... Xs>
344class function_invoke_base<Type, Return(Xs...) noexcept>
345: public function_base<Type, function_base_impl<Type, Return, true, Xs...>> {
346public:
347 template <class Fn>
348 using FnInvQuals = Fn&;
349
350 template <class 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...>;
353
354public:
355 using result_type = Return;
356
357 Return operator()(Xs... args) noexcept { return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
358};
359
360template <DerivedType Type, class Return, class... Xs>
361class function_invoke_base<Type, Return(Xs...) & noexcept>
362: public function_base<Type, function_base_impl<Type, Return, true, Xs...>> {
363public:
364 template <class Fn>
365 using FnInvQuals = Fn&;
366
367 template <class Fn>
368 static constexpr bool is_callable_from = std::is_nothrow_invocable_r_v<Return, Fn&, Xs...>;
369
370public:
371 using result_type = Return;
372
373 Return operator()(Xs... args) & noexcept { return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
374};
375
376template <DerivedType Type, class Return, class... Xs>
377class function_invoke_base<Type, Return(Xs...) && noexcept>
378: public function_base<Type, function_base_impl<Type, Return, true, Xs...>> {
379public:
380 template <class Fn>
381 using FnInvQuals = Fn&&;
382
383 template <class Fn>
384 static constexpr bool is_callable_from = std::is_nothrow_invocable_r_v<Return, Fn, Xs...>;
385
386public:
387 using result_type = Return;
388
389 Return operator()(Xs... args) && noexcept { return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...); }
390};
391
392template <DerivedType Type, class Return, class... Xs>
393class function_invoke_base<Type, Return(Xs...) const noexcept>
394: public function_base<Type, function_base_impl<Type, Return, true, Xs...>> {
395public:
396 template <class Fn>
397 using FnInvQuals = Fn const&;
398
399 template <class Fn>
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...>;
402
403public:
404 using result_type = Return;
405
406 Return operator()(Xs... args) const noexcept {
407 return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...);
408 }
409};
410
411template <DerivedType Type, class Return, class... Xs>
412class function_invoke_base<Type, Return(Xs...) const & noexcept>
413: public function_base<Type, function_base_impl<Type, Return, true, Xs...>> {
414public:
415 template <class Fn>
416 using FnInvQuals = Fn const&;
417
418 template <class Fn>
419 static constexpr bool is_callable_from = std::is_nothrow_invocable_r_v<Return, Fn const&, Xs...>;
420
421public:
422 using result_type = Return;
423
424 Return operator()(Xs... args) const& noexcept {
425 return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...);
426 }
427};
428
429template <DerivedType Type, class Return, class... Xs>
430class function_invoke_base<Type, Return(Xs...) const && noexcept>
431: public function_base<Type, function_base_impl<Type, Return, true, Xs...>> {
432public:
433 template <class Fn>
434 using FnInvQuals = Fn const&&;
435
436 template <class Fn>
437 static constexpr bool is_callable_from = std::is_nothrow_invocable_r_v<Return, Fn const, Xs...>;
438
439public:
440 using result_type = Return;
441
442 Return operator()(Xs... args) const&& noexcept {
443 return this->get_invoke()(this->mStorage, std::forward<Xs>(args)...);
444 }
445};
446
447template <DerivedType Type, class Signature>
448class function_invoke : public function_invoke_base<Type, Signature> {
450
451 template <class Fn>
452 static constexpr bool enable_one_arg_constructor =
453 !(std::is_same_v<std::remove_cvref_t<Fn>, function_invoke>
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>>;
457
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>>;
461
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>>;
466
467public:
468 function_invoke() = default;
469 function_invoke(std::nullptr_t) : function_invoke() {}
470 template <class F>
471 requires enable_one_arg_constructor<F>
472 function_invoke(F&& 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>;
477 }) {
478 if (f == nullptr) {
479 this->construct_empty();
480 return;
481 }
482 }
483 using FnInvQuals = base::template FnInvQuals<Fn>;
484 this->template construct_target<Fn, FnInvQuals>(std::forward<F>(f));
485 }
486
487 template <class F, class... Xs>
488 requires enable_in_place_constructor<F, Xs...>
489 explicit function_invoke(std::in_place_type_t<F>, Xs&&... args) {
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)...);
494 }
495
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)...);
503 }
504
505public:
506 void swap(function_invoke& other) {
507 function_invoke tmp = std::move(other);
508 other = std::move(*this);
509 *this = std::move(tmp);
510 }
511};
512
513} // namespace brstd::detail::function