Corium 1.1.0
High-Performance Zero-Heap C++20 MPSC Application Runtime
Loading...
Searching...
No Matches
FastDelegate.hpp
Go to the documentation of this file.
1
7#pragma once
8
9#include <cassert>
10#include <cstddef>
11#include <new> // IWYU pragma: keep
12#include <type_traits>
13#include <utility>
14
15namespace corium {
16namespace internal {
17
21template <typename EventType, std::size_t InlineSize = 32>
23 using StubFn = void (*)(void* instance, const EventType& event);
24 using DestroyFn = void (*)(void* instance) noexcept;
25 using MoveFn = void (*)(void* destStorage, void*& destInstance, void*& srcInstance) noexcept;
26
27public:
28 EventHandlerDelegate() noexcept = default;
29
33 template <
34 typename Handler,
35 typename Decayed = std::decay_t<Handler>,
36 typename = std::enable_if_t<
37 !std::is_same_v<Decayed, EventHandlerDelegate> &&
38 std::is_invocable_r_v<void, Decayed&, const EventType&>
39 >
40 >
41 explicit EventHandlerDelegate(Handler&& handler)
42 {
43 static_assert(
44 std::is_nothrow_destructible_v<Decayed>,
45 "Handler destructor must be noexcept"
46 );
47 static_assert(
48 sizeof(Decayed) <= InlineSize,
49 "Handler size exceeds FastDelegate inline storage size! Reduce captured state or increase InlineSize."
50 );
51 static_assert(
52 alignof(Decayed) <= alignof(std::max_align_t),
53 "Handler alignment requirement exceeds inline storage alignment."
54 );
55 static_assert(
56 std::is_nothrow_move_constructible_v<Decayed>,
57 "Handler must be nothrow move constructible."
58 );
59
60 void* storage = static_cast<void*>(_inlineStorage);
61 ::new (storage) Decayed(std::forward<Handler>(handler));
62 _instance = storage;
63
64 _destroy = [](void* instance) noexcept {
65 reinterpret_cast<Decayed*>(instance)->~Decayed();
66 };
67
68 _move = [](void* destStorage, void*& destInstance, void*& srcInstance) noexcept {
69 auto* src = reinterpret_cast<Decayed*>(srcInstance);
70 ::new (destStorage) Decayed(std::move(*src));
71 src->~Decayed();
72 destInstance = destStorage;
73 srcInstance = nullptr;
74 };
75
76 _stub = [](void* instance, const EventType& event) {
77 (*reinterpret_cast<Decayed*>(instance))(event);
78 };
79 }
80
83 void* srcObj,
84 void (*stub)(void* instance, const EventType& event),
85 void (*moveFn)(void* destStorage, void*& destInstance, void*& srcInstance) noexcept,
86 void (*destroyFn)(void* instance) noexcept
87 ) noexcept
88 {
89 void* storage = static_cast<void*>(_inlineStorage);
90 _destroy = destroyFn;
91 _move = moveFn;
92 _stub = stub;
93 if (_move) {
94 _move(storage, _instance, srcObj);
95 }
96 }
97
99 {
100 reset();
101 }
102
104 {
105 moveFrom(std::move(rhs));
106 }
107
109 {
110 if (this != &rhs) {
111 reset();
112 moveFrom(std::move(rhs));
113 }
114
115 return *this;
116 }
117
120
123 void invoke(const EventType& event) const
124 {
125 assert(_stub && "Invoking an empty EventHandlerDelegate");
126
127 if (!_stub) {
128 return;
129 }
130
131 _stub(_instance, event);
132 }
133
135 void operator()(const EventType& event) const
136 {
137 invoke(event);
138 }
139
141 explicit operator bool() const noexcept
142 {
143 return _stub != nullptr;
144 }
145
147 void reset() noexcept
148 {
149 if (_destroy) {
150 _destroy(_instance);
151 }
152
153 _instance = nullptr;
154 _stub = nullptr;
155 _destroy = nullptr;
156 _move = nullptr;
157 }
158
159private:
160 void moveFrom(EventHandlerDelegate&& rhs) noexcept
161 {
162 _stub = rhs._stub;
163 _destroy = rhs._destroy;
164 _move = rhs._move;
165
166 if (_move) {
167 _move(
168 static_cast<void*>(_inlineStorage),
169 _instance,
170 rhs._instance
171 );
172 } else {
173 _instance = nullptr;
174 }
175
176 rhs._stub = nullptr;
177 rhs._destroy = nullptr;
178 rhs._move = nullptr;
179 }
180
181private:
182 alignas(std::max_align_t) std::byte _inlineStorage[InlineSize > 0 ? InlineSize : 1]{};
183
184 void* _instance = nullptr;
185 StubFn _stub = nullptr;
186 DestroyFn _destroy = nullptr;
187 MoveFn _move = nullptr;
188};
189
191template <typename EventType, std::size_t InlineSize = 32>
193
194} // namespace internal
195
198
199} // namespace corium
Lightweight non-allocating delegate wrapper with 32-byte Small Buffer Optimization (SBO)....
Definition FastDelegate.hpp:22
void invoke(const EventType &event) const
Invoke wrapped handler directly with concrete event.
Definition FastDelegate.hpp:123
void operator()(const EventType &event) const
Function call operator for callable ergonomics.
Definition FastDelegate.hpp:135
~EventHandlerDelegate()
Definition FastDelegate.hpp:98
EventHandlerDelegate & operator=(EventHandlerDelegate &&rhs) noexcept
Definition FastDelegate.hpp:108
EventHandlerDelegate & operator=(const EventHandlerDelegate &)=delete
void reset() noexcept
Reset delegate to empty state.
Definition FastDelegate.hpp:147
EventHandlerDelegate(void *srcObj, void(*stub)(void *instance, const EventType &event), void(*moveFn)(void *destStorage, void *&destInstance, void *&srcInstance) noexcept, void(*destroyFn)(void *instance) noexcept) noexcept
Construct delegate from type-erased thunk operations.
Definition FastDelegate.hpp:82
EventHandlerDelegate(EventHandlerDelegate &&rhs) noexcept
Definition FastDelegate.hpp:103
EventHandlerDelegate(const EventHandlerDelegate &)=delete
Definition Application.hpp:16