Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
move_func.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cstddef>
5#include <cstdint>
6#include <new>
7#include <type_traits>
8#include <utility>
9
10#include "gaia/core/func.h"
11#include "gaia/mem/mem_alloc.h"
12#if GAIA_FUNC_WRAPPER_SMALLBLOCK
13 #include "gaia/mem/smallblock_allocator.h"
14#endif
15
16namespace gaia {
17 namespace util {
18 template <typename Signature>
19 class MoveFunc;
20
23 template <typename R, typename... Args>
24 class MoveFunc<R(Args...)> {
25 static constexpr uint32_t BufferSize = 24;
26
27 using InvokeFn = R (*)(const void*, Args&&...);
28 using DestroyFn = void (*)(void*);
29 using MoveFn = void (*)(MoveFunc&, MoveFunc&) noexcept;
30
31 struct Ops {
32 InvokeFn invoke;
33 DestroyFn destroy;
34 MoveFn move;
35 };
36
37 union Storage {
38 alignas(std::max_align_t) uint8_t inlineData[BufferSize];
39 void* pHeap;
40
41 constexpr Storage(): pHeap(nullptr) {}
42 };
43
44 void* m_ptr = nullptr;
45 const Ops* m_ops = nullptr;
46 Storage m_storage;
47
48 template <typename Fn>
49 static Fn* inline_ptr(MoveFunc& func) {
50 return reinterpret_cast<Fn*>(func.m_ptr);
51 }
52
53 template <typename Fn>
54 static const Fn* inline_ptr(const MoveFunc& func) {
55 return reinterpret_cast<const Fn*>(func.m_ptr);
56 }
57
58 template <typename Fn>
59 static Fn* ptr(const void* p) {
60 return reinterpret_cast<Fn*>(const_cast<void*>(p));
61 }
62
63 template <typename Fn>
64 static R invoke_inline(const void* p, Args&&... args) {
65 auto* pFn = ptr<Fn>(p);
66 GAIA_ASSERT(pFn != nullptr);
67 if constexpr (std::is_void_v<R>) {
68 gaia::invoke(*pFn, GAIA_FWD(args)...);
69 return;
70 } else
71 return gaia::invoke(*pFn, GAIA_FWD(args)...);
72 }
73
74 template <typename Fn>
75 static void destroy_inline(void* p) {
76 auto* pFn = ptr<Fn>(p);
77 GAIA_ASSERT(pFn != nullptr);
78 if constexpr (!std::is_trivially_destructible_v<Fn>)
79 pFn->~Fn();
80 }
81
82 template <typename Fn>
83 static void move_inline(MoveFunc& dst, MoveFunc& src) noexcept {
84 auto* pSrcFn = inline_ptr<Fn>(src);
85 GAIA_ASSERT(pSrcFn != nullptr);
86 new (dst.m_storage.inlineData) Fn(GAIA_MOV(*pSrcFn));
87 pSrcFn->~Fn();
88 dst.m_ptr = dst.m_storage.inlineData;
89 dst.m_ops = src.m_ops;
90 src.m_ptr = nullptr;
91 src.m_ops = nullptr;
92 }
93
94 template <typename Fn>
95 static R invoke_heap(const void* p, Args&&... args) {
96 auto* pFn = ptr<Fn>(p);
97 GAIA_ASSERT(pFn != nullptr);
98 if constexpr (std::is_void_v<R>) {
99 gaia::invoke(*pFn, GAIA_FWD(args)...);
100 return;
101 } else
102 return gaia::invoke(*pFn, GAIA_FWD(args)...);
103 }
104
105 template <typename Fn>
106 static void destroy_heap(void* p) {
107 auto* pFn = ptr<Fn>(p);
108 GAIA_ASSERT(pFn != nullptr);
109 if constexpr (!std::is_trivially_destructible_v<Fn>)
110 pFn->~Fn();
111 mem::AllocHelper::free(pFn);
112 }
113
114#if GAIA_FUNC_WRAPPER_SMALLBLOCK
115 template <typename Fn>
116 static void destroy_smallblock(void* p) {
117 auto* pFn = ptr<Fn>(p);
118 GAIA_ASSERT(pFn != nullptr);
119 if constexpr (!std::is_trivially_destructible_v<Fn>)
120 pFn->~Fn();
121 mem::SmallBlockAllocator::get().free(pFn);
122 }
123#endif
124
125 template <typename Fn>
126 static void move_heap(MoveFunc& dst, MoveFunc& src) noexcept {
127 dst.m_ptr = src.m_ptr;
128 dst.m_ops = src.m_ops;
129 src.m_ptr = nullptr;
130 src.m_ops = nullptr;
131 }
132
133 template <typename Fn>
134 static const Ops& inline_ops() {
135 static const Ops ops = {&invoke_inline<Fn>, &destroy_inline<Fn>, &move_inline<Fn>};
136 return ops;
137 }
138
139#if GAIA_FUNC_WRAPPER_SMALLBLOCK
140 template <typename Fn>
141 static const Ops& smallblock_ops() {
142 static const Ops ops = {&invoke_heap<Fn>, &destroy_smallblock<Fn>, &move_heap<Fn>};
143 return ops;
144 }
145#endif
146
147 template <typename Fn>
148 static const Ops& heap_ops() {
149 static const Ops ops = {&invoke_heap<Fn>, &destroy_heap<Fn>, &move_heap<Fn>};
150 return ops;
151 }
152
153 void destroy() {
154 if (m_ops != nullptr) {
155 m_ops->destroy(m_ptr);
156 m_ptr = nullptr;
157 m_ops = nullptr;
158 }
159 }
160
161 template <typename F>
162 void init(F&& f) {
163 using Fn = std::decay_t<F>;
164 static_assert(std::is_invocable_r_v<R, Fn&, Args...>, "MoveFunc requires a compatible callable");
165 static_assert(std::is_move_constructible_v<Fn>, "Callable must be move-constructible");
166 static_assert(
167 alignof(Fn) <= alignof(std::max_align_t), "Over-aligned callables are not supported for MoveFunc");
168
169 if constexpr (sizeof(Fn) <= BufferSize) {
170 new (m_storage.inlineData) Fn(GAIA_FWD(f));
171 m_ptr = m_storage.inlineData;
172 m_ops = &inline_ops<Fn>();
173#if GAIA_FUNC_WRAPPER_SMALLBLOCK
174 } else if constexpr (sizeof(Fn) <= mem::SmallBlockMaxSize) {
175 auto* pStorage = mem::SmallBlockAllocator::get().alloc((uint32_t)sizeof(Fn));
176 GAIA_ASSERT((uintptr_t)pStorage % alignof(Fn) == 0);
177 auto* pFunc = new (pStorage) Fn(GAIA_FWD(f));
178 m_ptr = pFunc;
179 m_ops = &smallblock_ops<Fn>();
180#endif
181 } else {
182 auto* pStorage = mem::AllocHelper::alloc<Fn>();
183 GAIA_ASSERT((uintptr_t)pStorage % alignof(Fn) == 0);
184 auto* pFunc = new (pStorage) Fn(GAIA_FWD(f));
185 m_ptr = pFunc;
186 m_ops = &heap_ops<Fn>();
187 }
188 }
189
190 public:
192 MoveFunc() = default;
193
195 MoveFunc(std::nullptr_t) {}
196
199 destroy();
200 }
201
202 MoveFunc(const MoveFunc&) = delete;
203 MoveFunc& operator=(const MoveFunc&) = delete;
204
207 MoveFunc(MoveFunc&& other) noexcept {
208 if (other.m_ops != nullptr)
209 other.m_ops->move(*this, other);
210 }
211
215 MoveFunc& operator=(MoveFunc&& other) noexcept {
216 if (this != &other) {
217 destroy();
218 if (other.m_ops != nullptr)
219 other.m_ops->move(*this, other);
220 }
221 return *this;
222 }
223
226 MoveFunc& operator=(std::nullptr_t) {
227 reset();
228 return *this;
229 }
230
231 template <typename F, typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, MoveFunc>>>
232 MoveFunc(F&& f) {
233 init(GAIA_FWD(f));
234 }
235
236 template <typename F, typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, MoveFunc>>>
237 MoveFunc& operator=(F&& f) {
238 destroy();
239 init(GAIA_FWD(f));
240 return *this;
241 }
242
247 template <typename F>
248 static MoveFunc create(F&& f) {
249 MoveFunc func;
250 func.init(GAIA_FWD(f));
251 return func;
252 }
253
256 static void destroy(MoveFunc& func) {
257 func.destroy();
258 }
259
262 R exec(Args... args) const {
263 GAIA_ASSERT(m_ops != nullptr);
264 if constexpr (std::is_void_v<R>) {
265 m_ops->invoke(m_ptr, GAIA_FWD(args)...);
266 return;
267 } else
268 return m_ops->invoke(m_ptr, GAIA_FWD(args)...);
269 }
270
273 R operator()(Args... args) const {
274 if constexpr (std::is_void_v<R>) {
275 exec(GAIA_FWD(args)...);
276 return;
277 } else
278 return exec(GAIA_FWD(args)...);
279 }
280
282 void reset() {
283 destroy();
284 }
285
287 explicit operator bool() const {
288 return m_ops != nullptr;
289 }
290 };
291 } // namespace util
292} // namespace gaia
R operator()(Args... args) const
Executes the stored callable.
Definition move_func.h:273
MoveFunc(std::nullptr_t)
Constructs an empty function wrapper.
Definition move_func.h:195
static void destroy(MoveFunc &func)
Destroys the callable held by the wrapper.
Definition move_func.h:256
void reset()
Clears the wrapper.
Definition move_func.h:282
static MoveFunc create(F &&f)
Creates a wrapper from a callable compatible with the function signature.
Definition move_func.h:248
~MoveFunc()
Destroys the stored callable, if any.
Definition move_func.h:198
MoveFunc & operator=(std::nullptr_t)
Clears the wrapper.
Definition move_func.h:226
MoveFunc()=default
Constructs an empty function wrapper.
R exec(Args... args) const
Executes the stored callable.
Definition move_func.h:262
MoveFunc(MoveFunc &&other) noexcept
Move-constructs the wrapper.
Definition move_func.h:207
MoveFunc & operator=(MoveFunc &&other) noexcept
Move-assigns the wrapper.
Definition move_func.h:215
Definition move_func.h:19
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9