19 {
20
21
22
23
24template <typename T> struct is_function_pointer {
25 static constexpr bool value = false;
26};
27
28template <typename R, typename... Args>
29struct is_function_pointer<R(*)(Args...)> {
30 static constexpr bool value = true;
31};
32
33
34
35
36
37
38
39
40
41
42template <typename> class function;
43
44
45
46template <typename R, typename... Args>
48private:
49 struct CallableBase {
51 virtual ~CallableBase() = default;
52 };
53
54 template <typename F>
55 struct Callable : CallableBase {
56 F f;
57 Callable(F fn) : f(fn) {}
59 };
60
61
62 struct FreeFunctionCallable {
63 R (*func_ptr)(Args...);
64
65 FreeFunctionCallable(R (*fp)(Args...)) : func_ptr(fp) {}
66
68 return func_ptr(
args...);
69 }
70 };
71
72
73
74
76
77 struct InlinedLambda {
78
79
80 alignas(max_align_t) char bytes[kInlineLambdaSize];
81
82
83 R (*invoker)(
const InlinedLambda& storage, Args...
args);
84 void (*destructor)(InlinedLambda& storage);
85
86 template <typename Function>
87 InlinedLambda(Function f) {
88 static_assert(sizeof(Function) <= kInlineLambdaSize,
89 "Lambda/functor too large for inline storage");
90 static_assert(alignof(Function) <= alignof(max_align_t),
91 "Lambda/functor requires stricter alignment than storage provides");
92
93
95
96
98
99
100 invoker = &invoke_lambda<Function>;
101 destructor = &destroy_lambda<Function>;
102 }
103
104
105 InlinedLambda(const InlinedLambda& other)
106 : invoker(other.invoker), destructor(other.destructor) {
107
108
109 fl::memcopy(bytes, other.bytes, kInlineLambdaSize);
110 }
111
112
113 InlinedLambda(InlinedLambda&& other)
114 : invoker(other.invoker), destructor(other.destructor) {
115 fl::memcopy(bytes, other.bytes, kInlineLambdaSize);
116
117 other.destructor = nullptr;
118 }
119
120 ~InlinedLambda() {
121 if (destructor) {
122 destructor(*this);
123 }
124 }
125
126 template <typename FUNCTOR>
127 static R invoke_lambda(
const InlinedLambda& storage, Args...
args) {
128
129 alignas(FUNCTOR) char temp_storage[sizeof(FUNCTOR)];
130
131 fl::memcopy(temp_storage, storage.bytes,
sizeof(FUNCTOR));
132
133 FUNCTOR* f = static_cast<FUNCTOR*>(static_cast<void*>(temp_storage));
134
135 return (*f)(
args...);
136 }
137
138 template <typename FUNCTOR>
139 static void destroy_lambda(InlinedLambda& storage) {
140
141
142
143
144
145
146
147 FUNCTOR* obj_ptr = static_cast<FUNCTOR*>(static_cast<void*>(storage.bytes));
148 obj_ptr->~FUNCTOR();
149 }
150
152 return invoker(*
this,
args...);
153 }
154 };
155
156
157 struct MemberCallableBase {
159 virtual ~MemberCallableBase() = default;
160 };
161
162
163 struct NonConstMemberCallable : MemberCallableBase {
164 void* obj;
165
166 union MemberFuncStorage {
167 char bytes[sizeof(R (NonConstMemberCallable::*)(Args...))];
168
169 void* alignment_dummy;
170 } member_func_storage;
171
172
173 R (*invoker)(
void* obj,
const MemberFuncStorage& mfp, Args...
args);
174
175 template <typename C>
176 NonConstMemberCallable(C* o, R (C::*mf)(Args...)) : obj(o) {
177
178 static_assert(sizeof(mf) <= sizeof(member_func_storage),
179 "Member function pointer too large");
180 fl::memcopy(member_func_storage.bytes, &mf,
sizeof(mf));
181
182 invoker = &invoke_nonconst_member<C>;
183 }
184
185 template <typename C>
186 static R invoke_nonconst_member(
void* obj,
const MemberFuncStorage& mfp, Args...
args) {
187 C* typed_obj = static_cast<C*>(obj);
188 R (C::*typed_mf)(Args...);
189 fl::memcopy(&typed_mf, mfp.bytes,
sizeof(typed_mf));
190 return (typed_obj->*typed_mf)(
args...);
191 }
192
194 return invoker(obj, member_func_storage,
args...);
195 }
196 };
197
198
199 struct ConstMemberCallable : MemberCallableBase {
200 const void* obj;
201
202 union MemberFuncStorage {
203 char bytes[sizeof(R (ConstMemberCallable::*)(Args...) const)];
204
205 void* alignment_dummy;
206 } member_func_storage;
207
208
209 R (*invoker)(
const void* obj,
const MemberFuncStorage& mfp, Args...
args);
210
211 template <typename C>
212 ConstMemberCallable(const C* o, R (C::*mf)(Args...) const) : obj(o) {
213
214 static_assert(sizeof(mf) <= sizeof(member_func_storage),
215 "Member function pointer too large");
216 fl::memcopy(member_func_storage.bytes, &mf,
sizeof(mf));
217
218 invoker = &invoke_const_member<C>;
219 }
220
221 template <typename C>
222 static R invoke_const_member(
const void* obj,
const MemberFuncStorage& mfp, Args...
args) {
223 const C* typed_obj = static_cast<const C*>(obj);
224 R (C::*typed_mf)(Args...) const;
225 fl::memcopy(&typed_mf, mfp.bytes,
sizeof(typed_mf));
226 return (typed_obj->*typed_mf)(
args...);
227 }
228
230 return invoker(obj, member_func_storage,
args...);
231 }
232 };
233
234
235 using Storage = Variant<fl::shared_ptr<CallableBase>, FreeFunctionCallable, InlinedLambda, NonConstMemberCallable, ConstMemberCallable>;
236 Storage storage_;
237
238
239 template<typename ReturnType>
240 typename enable_if<!is_void<ReturnType>::value, ReturnType>::type
241 default_return_helper() const {
242 return ReturnType{};
243 }
244
245 template<typename ReturnType>
246 typename enable_if<is_void<ReturnType>::value, ReturnType>::type
247 default_return_helper() const {
248 return;
249 }
250
251public:
252 function() = default;
253
254
255 function(const function& other) : storage_(other.storage_) {}
256
257
258 function(function&& other) noexcept : storage_(
fl::move(other.storage_)) {}
259
260
261 function& operator=(const function& other) {
262 if (this != &other) {
263 storage_ = other.storage_;
264 }
265 return *this;
266 }
267
268
269 function& operator=(function&& other) noexcept {
270 if (this != &other) {
271 storage_ =
fl::move(other.storage_);
272 }
273 return *this;
274 }
275
276
277 function(R (*fp)(Args...)) {
278 storage_ = FreeFunctionCallable(fp);
279 }
280
281
282 template <typename F, typename = enable_if_t<!is_member_function_pointer<F>::value && !is_function_pointer<F>::value>>
283 function(F f) {
284
285 construct_lambda_or_functor(
fl::move(f),
typename conditional<
sizeof(F) <= kInlineLambdaSize, true_type, false_type>::type{});
286 }
287
288
289 template <typename C>
290 function(R (C::*mf)(Args...), C* obj) {
291 storage_ = NonConstMemberCallable(obj, mf);
292 }
293
294
295 template <typename C>
296 function(R (C::*mf)(Args...) const, const C* obj) {
297 storage_ = ConstMemberCallable(obj, mf);
298 }
299
300 R operator()(Args...
args)
const {
301
302 if (auto* heap_callable = storage_.template ptr<fl::shared_ptr<CallableBase>>()) {
303 return (*heap_callable)->invoke(
args...);
304 } else if (auto* free_func = storage_.template ptr<FreeFunctionCallable>()) {
305 return free_func->invoke(
args...);
306 } else if (auto* inlined_lambda = storage_.template ptr<InlinedLambda>()) {
307 return inlined_lambda->invoke(
args...);
308 } else if (auto* nonconst_member = storage_.template ptr<NonConstMemberCallable>()) {
309 return nonconst_member->invoke(
args...);
310 } else if (auto* const_member = storage_.template ptr<ConstMemberCallable>()) {
311 return const_member->invoke(
args...);
312 }
313
314 return default_return_helper<R>();
315 }
316
317 explicit operator bool() const {
318 return !storage_.empty();
319 }
320
322 storage_ = Storage{};
323 }
324
326
327
328 return storage_.empty() == o.storage_.empty();
329 }
330
332 return !(*this == o);
333 }
334
335private:
336
337 template <typename F>
338 void construct_lambda_or_functor(F f, true_type ) {
339 storage_ = InlinedLambda(
fl::move(f));
340 }
341
342
343 template <typename F>
344 void construct_lambda_or_functor(F f, false_type ) {
346 }
347};
348
349}
FASTLED_FORCE_INLINE bool operator!=(const CRGB &lhs, const CRGB &rhs)
Check if two CRGB objects do not have the same color data.
FASTLED_FORCE_INLINE bool operator==(const CRGB &lhs, const CRGB &rhs)
Check if two CRGB objects have the same color data.
#define FASTLED_INLINE_LAMBDA_SIZE
constexpr remove_reference< T >::type && move(T &&t) noexcept
auto invoke(F &&f, T1 &&t1, Args &&... args) -> enable_if_t< is_member_function_pointer< typename remove_reference< F >::type >::value &&!detail::use_pointer_syntax< T1 >::value, decltype((fl::forward< T1 >(t1).*f)(fl::forward< Args >(args)...))>
void * memcopy(void *dst, const void *src, fl::size num)
void clear(CRGB(&arr)[N])
void * memfill(void *ptr, int value, fl::size num)
shared_ptr< T > make_shared(Args &&... args)