LeviLamina
Loading...
Searching...
No Matches
function_base_impl.h
1#pragma once
2
3#include <functional>
4
5#include "mc/platform/brstd/detail/DerivedType.h"
6
7namespace brstd::detail::function {
8
9constexpr size_t embedded_target_size = sizeof(void*) * 8 - sizeof(void*);
10
11template <DerivedType Type, class Return, bool Noexcept, class... Xs>
13 class storage;
14
15 template <DerivedType Type>
16 struct vtable_base {
17 void (*move_to)(storage&, storage&) noexcept;
18 void (*destroy)(storage&) noexcept;
19 Return (*invoke)(storage const&, Xs&&...) noexcept(Noexcept);
20 };
21
22 template <>
23 struct vtable_base<DerivedType::Copyable> : vtable_base<DerivedType::MoveOnly> {
24 void (*copy_to)(storage const&, storage&);
25 };
26
27 struct vtable : vtable_base<Type> {};
28
29 template <class Fn>
30 static constexpr size_t embedded_target_offset =
31 alignof(Fn) <= sizeof(vtable*) ? 0 : (alignof(Fn) - sizeof(vtable*));
32
33 template <class Fn>
34 static constexpr size_t embedded_target_available_size = embedded_target_size - embedded_target_offset<Fn>;
35
36 template <class Fn>
37 static constexpr bool is_heap_target = alignof(Fn) > alignof(std::max_align_t)
38 || sizeof(Fn) > embedded_target_available_size<Fn>
39 || !std::is_nothrow_move_constructible_v<Fn>;
40
41 class storage {
42 public:
43 vtable const* vfptr;
44
45 private:
46 union alignas(max_align_t) {
47 void* heap_target;
48 char embedded_target[embedded_target_size];
49 };
50
51 public:
52 template <class Fn>
53 [[nodiscard]] void* embedded_target_ptr() noexcept {
54 return &embedded_target + embedded_target_offset<Fn>;
55 }
56
57 template <class Fn>
58 [[nodiscard]] Fn* small_fn_ptr() const noexcept {
59 return static_cast<Fn*>(const_cast<storage*>(this)->embedded_target_ptr<Fn>());
60 }
61
62 template <class Fn>
63 [[nodiscard]] Fn* large_fn_ptr() const noexcept {
64 return static_cast<Fn*>(heap_target);
65 }
66
67 void set_large_fn_ptr(void* const v) noexcept { heap_target = v; }
68 };
69 template <class Fn, class FnInvQuals, bool HeapTarget>
70 struct vtable_impl {
71 static Fn* target(storage const& self) {
72 if constexpr (HeapTarget) {
73 return self.large_fn_ptr<Fn>();
74 } else {
75 return self.small_fn_ptr<Fn>();
76 }
77 }
78
79 static void copy_to(storage const& self, storage& to) {
80 if constexpr (Type == DerivedType::Copyable) {
81 if constexpr (HeapTarget) {
82 to.set_large_fn_ptr(::new Fn(*target(self)));
83 } else {
84 ::new (to.embedded_target_ptr<Fn>()) Fn(*target(self));
85 }
86 }
87 }
88
89 static void move_to(storage& self, storage& to) noexcept {
90 if constexpr (HeapTarget) {
91 to.set_large_fn_ptr(target(self));
92 self.set_large_fn_ptr(nullptr);
93 } else {
94 auto const ptr = target(self);
95 ::new (to.embedded_target_ptr<Fn>()) Fn(std::move(*ptr));
96 ptr->~Fn();
97 }
98 }
99
100 static void destroy(storage& self) noexcept {
101 if constexpr (HeapTarget) {
102 delete target(self);
103 } else {
104 target(self)->~Fn();
105 }
106 }
107
108 static Return invoke(storage const& self, Xs&&... args) noexcept(Noexcept) {
109 if constexpr (std::is_void_v<Return>) {
110 (void)std::invoke(static_cast<FnInvQuals>(*target(self)), std::forward<Xs>(args)...);
111 } else {
112 return std::invoke(static_cast<FnInvQuals>(*target(self)), std::forward<Xs>(args)...);
113 }
114 }
115 };
116
117 template <class Fn, class FnInvQuals>
118 [[nodiscard]] static constexpr vtable create_vtable() noexcept {
119 vtable impl{};
120
121 using impl_type = vtable_impl<Fn, FnInvQuals, is_heap_target<Fn>>;
122 impl.move_to = &impl_type::move_to;
123 impl.destroy = &impl_type::destroy;
124 impl.invoke = &impl_type::invoke;
125 if constexpr (Type == DerivedType::Copyable) {
126 impl.copy_to = &impl_type::copy_to;
127 }
128 return impl;
129 }
130
131 template <class Fn, class FnInvQuals>
132 static constexpr vtable vfstorage = create_vtable<Fn, FnInvQuals>();
133
134protected:
135 storage mStorage;
136
137 vtable const& get_vtable() const noexcept { return *mStorage.vfptr; }
138
139 function_base_impl() noexcept = default;
140
141 void construct_empty() { mStorage.vfptr = nullptr; }
142 template <class Fn, class FnInvQuals, class... Ys>
143 void construct_target(Ys&&... args) {
144 mStorage.vfptr = &vfstorage<Fn, FnInvQuals>;
145 if constexpr (is_heap_target<Fn>) {
146 auto ptr = std::make_unique<Fn>(std::forward<Ys>(args)...);
147 mStorage.set_large_fn_ptr(ptr.release());
148 } else {
149 ::new (mStorage.embedded_target_ptr<Fn>()) Fn(std::forward<Ys>(args)...);
150 }
151 }
152
153 auto get_invoke() const noexcept {
154 if (!*this) {
155 std::abort();
156 }
157 return get_vtable().invoke;
158 }
159
160public:
161 explicit operator bool() const noexcept { return mStorage.vfptr != nullptr; }
162};
163
164} // namespace brstd::detail::function
Definition function_base_impl.h:12