20 static constexpr uint32_t BufferSize = 24;
22 enum class Op : uint8_t { Invoke, Destroy, Move };
25 OpFn m_func =
nullptr;
26 alignas(std::max_align_t) uint8_t m_storage[BufferSize]{};
28#if GAIA_FUNC_WRAPPER_SMALLBLOCK
29 template <
typename Fn>
31 auto*& pFn = *
reinterpret_cast<Fn**
>(dst->m_storage);
35 GAIA_ASSERT(pFn !=
nullptr);
39 GAIA_ASSERT(pFn !=
nullptr);
41 mem::SmallBlockAllocator::get().free(pFn);
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;
56 if (m_func !=
nullptr) {
57 m_func(Op::Destroy,
this,
nullptr);
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");
68 if constexpr (
sizeof(Fn) <= BufferSize &&
alignof(Fn) <=
alignof(std::max_align_t)) {
69 new (m_storage) Fn(GAIA_FWD(f));
72 auto* pFn =
reinterpret_cast<Fn*
>(dst->m_storage);
82 GAIA_ASSERT(src !=
nullptr);
83 auto* pSrcFn =
reinterpret_cast<Fn*
>(src->m_storage);
84 new (dst->m_storage) Fn(GAIA_MOV(*pSrcFn));
86 dst->m_func = src->m_func;
87 src->m_func =
nullptr;
94 alignof(Fn) <=
alignof(std::max_align_t),
"Over-aligned callables are not supported for SmallFunc");
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));
102 pStorage = mem::AllocHelper::alloc<Fn>();
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>;
114 auto*& pFn = *
reinterpret_cast<Fn**
>(dst->m_storage);
118 GAIA_ASSERT(pFn !=
nullptr);
122 GAIA_ASSERT(pFn !=
nullptr);
124 mem::AllocHelper::free(pFn);
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;
155 if (other.m_func !=
nullptr)
156 other.m_func(Op::Move,
this, &other);
163 if (
this != &other) {
165 if (other.m_func !=
nullptr)
166 other.m_func(Op::Move,
this, &other);
171 template <
typename F,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, SmallFunc>>>
176 template <
typename F,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, SmallFunc>>>
187 template <
typename F>
190 func.init(GAIA_FWD(f));
202 GAIA_ASSERT(m_func !=
nullptr);
203 m_func(Op::Invoke,
this,
nullptr);
217 explicit operator bool()
const {
218 return m_func !=
nullptr;