Gaia-ECS v1.0.0
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();
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();
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:
191 MoveFunc() = default;
192
194 MoveFunc(std::nullptr_t) {}
195
198 destroy();
199 }
200
201 MoveFunc(const MoveFunc&) = delete;
202 MoveFunc& operator=(const MoveFunc&) = delete;
203
206 MoveFunc(MoveFunc&& other) noexcept {
207 if (other.m_ops != nullptr)
208 other.m_ops->move(*this, other);
209 }
210
214 MoveFunc& operator=(MoveFunc&& other) noexcept {
215 if (this != &other) {
216 destroy();
217 if (other.m_ops != nullptr)
218 other.m_ops->move(*this, other);
219 }
220 return *this;
221 }
222
225 MoveFunc& operator=(std::nullptr_t) {
226 reset();
227 return *this;
228 }
229
233 template <typename F, typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, MoveFunc>>>
234 MoveFunc(F&& f) {
235 init(GAIA_FWD(f));
236 }
237
242 template <typename F, typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, MoveFunc>>>
244 destroy();
245 init(GAIA_FWD(f));
246 return *this;
247 }
248
253 template <typename F>
254 static MoveFunc create(F&& f) {
255 MoveFunc func;
256 func.init(GAIA_FWD(f));
257 return func;
258 }
259
262 static void destroy(MoveFunc& func) {
263 func.destroy();
264 }
265
269 R exec(Args... args) const {
270 GAIA_ASSERT(m_ops != nullptr);
271 if constexpr (std::is_void_v<R>) {
272 m_ops->invoke(m_ptr, GAIA_FWD(args)...);
273 return;
274 } else
275 return m_ops->invoke(m_ptr, GAIA_FWD(args)...);
276 }
277
281 R operator()(Args... args) const {
282 if constexpr (std::is_void_v<R>) {
283 exec(GAIA_FWD(args)...);
284 return;
285 } else
286 return exec(GAIA_FWD(args)...);
287 }
288
290 void reset() {
291 destroy();
292 }
293
295 explicit operator bool() const {
296 return m_ops != nullptr;
297 }
298 };
299 } // namespace util
300} // namespace gaia
static T & get() noexcept
Returns the process-wide singleton object.
Definition dyn_singleton.h:37
R operator()(Args... args) const
Executes the stored callable.
Definition move_func.h:281
MoveFunc(std::nullptr_t)
Constructs an empty function wrapper.
Definition move_func.h:194
static void destroy(MoveFunc &func)
Destroys the callable held by the wrapper.
Definition move_func.h:262
void reset()
Clears the wrapper.
Definition move_func.h:290
static MoveFunc create(F &&f)
Creates a wrapper from a callable compatible with the function signature.
Definition move_func.h:254
~MoveFunc()
Destroys the stored callable, if any.
Definition move_func.h:197
MoveFunc & operator=(std::nullptr_t)
Clears the wrapper.
Definition move_func.h:225
R exec(Args... args) const
Executes the stored callable.
Definition move_func.h:269
MoveFunc & operator=(F &&f)
Replaces the stored callable.
Definition move_func.h:243
MoveFunc(MoveFunc &&other) noexcept
Move-constructs the wrapper.
Definition move_func.h:206
MoveFunc(F &&f)
Constructs a wrapper from a compatible callable.
Definition move_func.h:234
MoveFunc & operator=(MoveFunc &&other) noexcept
Move-assigns the wrapper.
Definition move_func.h:214
Definition move_func.h:19
static void free(void *ptr)
Releases storage allocated through an adaptor.
Definition mem_alloc.h:276