join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
function.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_CORE_FUNCTION_HPP
26#define JOIN_CORE_FUNCTION_HPP
27
28// C++.
29#include <type_traits>
30#include <functional>
31#include <utility>
32#include <new>
33
34// C.
35#include <cstddef>
36#include <cstring>
37
38namespace join
39{
43 template <typename Signature, std::size_t Capacity = 32, std::size_t Alignment = alignof (std::max_align_t)>
44 class Function;
45
49 template <typename Return, typename... Args, std::size_t Capacity, std::size_t Alignment>
50 class Function<Return (Args...), Capacity, Alignment>
51 {
52 public:
56 Function () noexcept = default;
57
62 Function (const Function& other) = delete;
63
69 Function& operator= (const Function& other) = delete;
70
75 Function (Function&& other) noexcept
76 {
77 moveFrom (std::move (other));
78 }
79
85 Function& operator= (Function&& other) noexcept
86 {
87 clear ();
88 moveFrom (std::move (other));
89 return *this;
90 }
91
95 Function (std::nullptr_t) noexcept
96 : Function ()
97 {
98 }
99
103 Function& operator= (std::nullptr_t) noexcept
104 {
105 clear ();
106 return *this;
107 }
108
113 template <typename Func, typename DecayedFunc = std::decay_t<Func>>
114 Function (Func&& callable)
115 requires (!std::is_same_v<DecayedFunc, Function>) && std::is_invocable_r_v<Return, DecayedFunc&, Args...>
116 {
117 static_assert (sizeof (DecayedFunc) <= Capacity, "Callable size exceeds Function capacity.");
118 static_assert (alignof (DecayedFunc) <= Alignment, "Callable alignment exceeds Function alignment.");
119 static_assert (std::is_nothrow_move_constructible_v<DecayedFunc>,
120 "Callable must be nothrow move constructible.");
121
122 new (&_storage) DecayedFunc (std::forward<Func> (callable));
123
124 _invoker = [] (void* storage, Args&&... args) -> Return {
125 return static_cast<Return> ((*static_cast<DecayedFunc*> (storage)) (std::forward<Args> (args)...));
126 };
127
128 _manager = nullptr;
129 _destructor = nullptr;
130
131 if constexpr (!std::is_trivially_copyable_v<DecayedFunc>)
132 {
133 _manager = [] (void* dst, void* src) noexcept {
134 DecayedFunc* source = static_cast<DecayedFunc*> (src);
135 new (dst) DecayedFunc (std::move (*source));
136 source->~DecayedFunc ();
137 };
138 }
139
140 if constexpr (!std::is_trivially_destructible_v<DecayedFunc>)
141 {
142 _destructor = [] (void* storage) noexcept {
143 static_cast<DecayedFunc*> (storage)->~DecayedFunc ();
144 };
145 }
146
147 // an empty target holds no state, so relocating it copies nothing at all and never
148 // reads the storage, which is left uninitialized by the placement new above.
149 _size = std::is_empty_v<DecayedFunc> ? 0 : sizeof (DecayedFunc);
150 }
151
156 {
157 clear ();
158 }
159
166 Return operator() (Args... args) const
167 {
168 if (!_invoker)
169 {
170 throw std::bad_function_call ();
171 }
172 return _invoker (&_storage, std::forward<Args> (args)...);
173 }
174
179 explicit operator bool () const noexcept
180 {
181 return _invoker != nullptr;
182 }
183
187 void reset () noexcept
188 {
189 clear ();
190 }
191
195 void swap (Function& other) noexcept
196 {
197 Function tmp (std::move (other));
198 other = std::move (*this);
199 *this = std::move (tmp);
200 }
201
202 private:
204 using InvokerFunc = Return (*) (void*, Args&&...);
205
207 using ManagerFunc = void (*) (void*, void*);
208
210 using DestructorFunc = void (*) (void*);
211
215 void clear () noexcept
216 {
217 if (_invoker == nullptr)
218 {
219 return;
220 }
221
222 if (_destructor)
223 {
224 _destructor (&_storage);
225 }
226
227 _invoker = nullptr;
228 _manager = nullptr;
229 _destructor = nullptr;
230 _size = 0;
231 }
232
236 void moveFrom (Function&& other) noexcept
237 {
238 _invoker = other._invoker;
239 _manager = other._manager;
240 _destructor = other._destructor;
241 _size = other._size;
242
243 if (_invoker == nullptr)
244 {
245 return;
246 }
247
248 if (_manager)
249 {
250 _manager (&_storage, &other._storage);
251 }
252 else
253 {
254 std::memcpy (&_storage, &other._storage, _size);
255 }
256
257 other._invoker = nullptr;
258 other._manager = nullptr;
259 other._destructor = nullptr;
260 other._size = 0;
261 }
262
264 alignas (Alignment) mutable unsigned char _storage[Capacity];
265
267 InvokerFunc _invoker = nullptr;
268
270 ManagerFunc _manager = nullptr;
271
273 DestructorFunc _destructor = nullptr;
274
276 std::size_t _size = 0;
277 };
278}
279
280#endif
void reset() noexcept
clear the stored callable.
Definition function.hpp:187
Function(std::nullptr_t) noexcept
construct from nullptr.
Definition function.hpp:95
Function(Func &&callable)
construct with a callable object.
Definition function.hpp:114
void swap(Function &other) noexcept
swap two Function instances.
Definition function.hpp:195
~Function()
destructor.
Definition function.hpp:155
Function() noexcept=default
default constructor.
fixed-capacity move-only allocation-free alternative to std::function.
Definition function.hpp:44
Definition acceptor.hpp:32