25 static constexpr uint32_t BufferSize = 24;
27 using InvokeFn = R (*)(
const void*, Args&&...);
28 using DestroyFn = void (*)(
void*);
38 alignas(std::max_align_t) uint8_t inlineData[BufferSize];
41 constexpr Storage(): pHeap(
nullptr) {}
44 void* m_ptr =
nullptr;
45 const Ops* m_ops =
nullptr;
48 template <
typename Fn>
49 static Fn* inline_ptr(
MoveFunc& func) {
50 return reinterpret_cast<Fn*
>(func.m_ptr);
53 template <
typename Fn>
54 static const Fn* inline_ptr(
const MoveFunc& func) {
55 return reinterpret_cast<const Fn*
>(func.m_ptr);
58 template <
typename Fn>
59 static Fn* ptr(
const void* p) {
60 return reinterpret_cast<Fn*
>(
const_cast<void*
>(p));
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)...);
71 return gaia::invoke(*pFn, GAIA_FWD(args)...);
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>)
82 template <
typename Fn>
84 auto* pSrcFn = inline_ptr<Fn>(src);
85 GAIA_ASSERT(pSrcFn !=
nullptr);
86 new (dst.m_storage.inlineData) Fn(GAIA_MOV(*pSrcFn));
88 dst.m_ptr = dst.m_storage.inlineData;
89 dst.m_ops = src.m_ops;
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)...);
102 return gaia::invoke(*pFn, GAIA_FWD(args)...);
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>)
111 mem::AllocHelper::free(pFn);
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>)
121 mem::SmallBlockAllocator::get().free(pFn);
125 template <
typename Fn>
127 dst.m_ptr = src.m_ptr;
128 dst.m_ops = src.m_ops;
133 template <
typename Fn>
134 static const Ops& inline_ops() {
135 static const Ops ops = {&invoke_inline<Fn>, &destroy_inline<Fn>, &move_inline<Fn>};
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>};
147 template <
typename Fn>
148 static const Ops& heap_ops() {
149 static const Ops ops = {&invoke_heap<Fn>, &destroy_heap<Fn>, &move_heap<Fn>};
154 if (m_ops !=
nullptr) {
155 m_ops->destroy(m_ptr);
161 template <
typename 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");
167 alignof(Fn) <=
alignof(std::max_align_t),
"Over-aligned callables are not supported for MoveFunc");
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));
179 m_ops = &smallblock_ops<Fn>();
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));
186 m_ops = &heap_ops<Fn>();
208 if (other.m_ops !=
nullptr)
209 other.m_ops->move(*
this, other);
216 if (
this != &other) {
218 if (other.m_ops !=
nullptr)
219 other.m_ops->move(*
this, other);
231 template <
typename F,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, MoveFunc>>>
236 template <
typename F,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, MoveFunc>>>
237 MoveFunc& operator=(F&& f) {
247 template <
typename F>
250 func.init(GAIA_FWD(f));
263 GAIA_ASSERT(m_ops !=
nullptr);
264 if constexpr (std::is_void_v<R>) {
265 m_ops->invoke(m_ptr, GAIA_FWD(args)...);
268 return m_ops->invoke(m_ptr, GAIA_FWD(args)...);
274 if constexpr (std::is_void_v<R>) {
275 exec(GAIA_FWD(args)...);
278 return exec(GAIA_FWD(args)...);
287 explicit operator bool()
const {
288 return m_ops !=
nullptr;