Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
small_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/mem/mem_alloc.h"
11#if GAIA_FUNC_WRAPPER_SMALLBLOCK
12 #include "gaia/mem/smallblock_allocator.h"
13#endif
14
15namespace gaia {
16 namespace util {
19 class SmallFunc {
20 static constexpr uint32_t BufferSize = 24;
21
22 enum class Op : uint8_t { Invoke, Destroy, Move };
23 using OpFn = void (*)(Op op, SmallFunc* dst, SmallFunc* src);
24
25 OpFn m_func = nullptr;
26 alignas(std::max_align_t) uint8_t m_storage[BufferSize]{};
27
28#if GAIA_FUNC_WRAPPER_SMALLBLOCK
29 template <typename Fn>
30 static void op_smallblock(Op op, SmallFunc* dst, SmallFunc* src) {
31 auto*& pFn = *reinterpret_cast<Fn**>(dst->m_storage);
32
33 switch (op) {
34 case Op::Invoke:
35 GAIA_ASSERT(pFn != nullptr);
36 (*pFn)();
37 break;
38 case Op::Destroy:
39 GAIA_ASSERT(pFn != nullptr);
40 pFn->~Fn();
41 mem::SmallBlockAllocator::get().free(pFn);
42 pFn = nullptr;
43 break;
44 case Op::Move:
45 GAIA_ASSERT(src != nullptr);
46 *reinterpret_cast<Fn**>(dst->m_storage) = *reinterpret_cast<Fn**>(src->m_storage);
47 dst->m_func = src->m_func;
48 *reinterpret_cast<Fn**>(src->m_storage) = nullptr;
49 src->m_func = nullptr;
50 break;
51 }
52 }
53#endif
54
55 void destroy() {
56 if (m_func != nullptr) {
57 m_func(Op::Destroy, this, nullptr);
58 m_func = nullptr;
59 }
60 }
61
62 template <typename F>
63 void init(F&& f) {
64 using Fn = std::decay_t<F>;
65 static_assert(std::is_invocable_r_v<void, Fn&>, "SmallFunc requires a callable compatible with void()");
66 static_assert(std::is_move_constructible_v<Fn>, "Callable must be move-constructible");
67
68 if constexpr (sizeof(Fn) <= BufferSize && alignof(Fn) <= alignof(std::max_align_t)) {
69 new (m_storage) Fn(GAIA_FWD(f));
70
71 m_func = [](Op op, SmallFunc* dst, SmallFunc* src) {
72 auto* pFn = reinterpret_cast<Fn*>(dst->m_storage);
73
74 switch (op) {
75 case Op::Invoke:
76 (*pFn)();
77 break;
78 case Op::Destroy:
79 pFn->~Fn();
80 break;
81 case Op::Move: {
82 GAIA_ASSERT(src != nullptr);
83 auto* pSrcFn = reinterpret_cast<Fn*>(src->m_storage);
84 new (dst->m_storage) Fn(GAIA_MOV(*pSrcFn));
85 pSrcFn->~Fn();
86 dst->m_func = src->m_func;
87 src->m_func = nullptr;
88 break;
89 }
90 }
91 };
92 } else {
93 static_assert(
94 alignof(Fn) <= alignof(std::max_align_t), "Over-aligned callables are not supported for SmallFunc");
95
96 void* pStorage = nullptr;
97#if GAIA_FUNC_WRAPPER_SMALLBLOCK
98 if constexpr (sizeof(Fn) <= mem::SmallBlockMaxSize)
99 pStorage = mem::SmallBlockAllocator::get().alloc((uint32_t)sizeof(Fn));
100 else
101#endif
102 pStorage = mem::AllocHelper::alloc<Fn>();
103
104 GAIA_ASSERT((uintptr_t)pStorage % alignof(Fn) == 0);
105 auto* pFunc = new (pStorage) Fn(GAIA_FWD(f));
106 *reinterpret_cast<Fn**>(m_storage) = pFunc;
107#if GAIA_FUNC_WRAPPER_SMALLBLOCK
108 if constexpr (sizeof(Fn) <= mem::SmallBlockMaxSize) {
109 m_func = &op_smallblock<Fn>;
110 } else
111#endif
112 {
113 m_func = [](Op op, SmallFunc* dst, SmallFunc* src) {
114 auto*& pFn = *reinterpret_cast<Fn**>(dst->m_storage);
115
116 switch (op) {
117 case Op::Invoke:
118 GAIA_ASSERT(pFn != nullptr);
119 (*pFn)();
120 break;
121 case Op::Destroy:
122 GAIA_ASSERT(pFn != nullptr);
123 pFn->~Fn();
124 mem::AllocHelper::free(pFn);
125 pFn = nullptr;
126 break;
127 case Op::Move:
128 GAIA_ASSERT(src != nullptr);
129 *reinterpret_cast<Fn**>(dst->m_storage) = *reinterpret_cast<Fn**>(src->m_storage);
130 dst->m_func = src->m_func;
131 *reinterpret_cast<Fn**>(src->m_storage) = nullptr;
132 src->m_func = nullptr;
133 break;
134 }
135 };
136 }
137 }
138 }
139
140 public:
142 SmallFunc() = default;
143
146 destroy();
147 }
148
149 SmallFunc(const SmallFunc&) = delete;
150 SmallFunc& operator=(const SmallFunc&) = delete;
151
154 SmallFunc(SmallFunc&& other) noexcept {
155 if (other.m_func != nullptr)
156 other.m_func(Op::Move, this, &other);
157 }
158
162 SmallFunc& operator=(SmallFunc&& other) noexcept {
163 if (this != &other) {
164 destroy();
165 if (other.m_func != nullptr)
166 other.m_func(Op::Move, this, &other);
167 }
168 return *this;
169 }
170
171 template <typename F, typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, SmallFunc>>>
172 SmallFunc(F&& f) {
173 init(GAIA_FWD(f));
174 }
175
176 template <typename F, typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, SmallFunc>>>
177 SmallFunc& operator=(F&& f) {
178 destroy();
179 init(GAIA_FWD(f));
180 return *this;
181 }
182
187 template <typename F>
188 static SmallFunc create(F&& f) {
189 SmallFunc func;
190 func.init(GAIA_FWD(f));
191 return func;
192 }
193
196 static void destroy(SmallFunc& func) {
197 func.destroy();
198 }
199
201 void exec() {
202 GAIA_ASSERT(m_func != nullptr);
203 m_func(Op::Invoke, this, nullptr);
204 }
205
207 void operator()() {
208 exec();
209 }
210
212 void reset() {
213 destroy();
214 }
215
217 explicit operator bool() const {
218 return m_func != nullptr;
219 }
220 };
221
222#if __cplusplus < 202002L
223 #define CreateSmallFunc(x) \
224 ::gaia::util::SmallFunc::create([y = GAIA_MOV(x)]() { \
225 y(); \
226 })
227#else
228 #define CreateSmallFunc(x) ::gaia::util::SmallFunc::create(GAIA_MOV(x))
229#endif
230 } // namespace util
231} // namespace gaia
Move-only function wrapper with inline storage and optional SmallBlockAllocator spill storage....
Definition small_func.h:19
static SmallFunc create(F &&f)
Creates a wrapper from a callable compatible with void().
Definition small_func.h:188
void reset()
Clears the wrapper.
Definition small_func.h:212
~SmallFunc()
Destroys the stored callable, if any.
Definition small_func.h:145
SmallFunc & operator=(SmallFunc &&other) noexcept
Move-assigns the wrapper.
Definition small_func.h:162
static void destroy(SmallFunc &func)
Destroys the callable held by the wrapper.
Definition small_func.h:196
SmallFunc()=default
Constructs an empty function wrapper.
void operator()()
Executes the stored callable.
Definition small_func.h:207
void exec()
Executes the stored callable.
Definition small_func.h:201
SmallFunc(SmallFunc &&other) noexcept
Move-constructs the wrapper.
Definition small_func.h:154
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9