FastLED 3.9.15
Loading...
Searching...
No Matches

◆ FL_DISABLE_WARNING()

FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING ( float- conversion)

Definition at line 17 of file function.h.

19 {
20
21//----------------------------------------------------------------------------
22// is_function_pointer trait - detects function pointers like R(*)(Args...)
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// More or less a drop in replacement for std::function
35// function<R(Args...)>: type‐erasing "std::function" replacement
36// Supports free functions, lambdas/functors, member functions (const &
37// non‑const)
38//
39// NEW: Uses inline storage for member functions, free functions, and small
40// lambdas/functors. Only large lambdas/functors use heap allocation.
41//----------------------------------------------------------------------------
42template <typename> class function;
43
44
45
46template <typename R, typename... Args>
47class FL_ALIGN function<R(Args...)> {
48private:
49 struct CallableBase {
50 virtual R invoke(Args... args) = 0;
51 virtual ~CallableBase() = default;
52 };
53
54 template <typename F>
55 struct Callable : CallableBase {
56 F f;
57 Callable(F fn) : f(fn) {}
58 R invoke(Args... args) override { return f(args...); }
59 };
60
61 // Type-erased free function callable - stored inline!
62 struct FreeFunctionCallable {
63 R (*func_ptr)(Args...);
64
65 FreeFunctionCallable(R (*fp)(Args...)) : func_ptr(fp) {}
66
67 R invoke(Args... args) const {
68 return func_ptr(args...);
69 }
70 };
71
72 // Type-erased small lambda/functor callable - stored inline!
73 // Size limit for inline storage - configurable via preprocessor define
74
75 static constexpr fl::size kInlineLambdaSize = FASTLED_INLINE_LAMBDA_SIZE;
76
77 struct InlinedLambda {
78 // Storage for the lambda/functor object
79 // Use aligned storage to ensure proper alignment for any type
80 alignas(max_align_t) char bytes[kInlineLambdaSize];
81
82 // Type-erased invoker and destructor function pointers
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 // Initialize the entire storage to zero to avoid copying uninitialized memory
94 fl::memfill(bytes, 0, kInlineLambdaSize);
95
96 // Construct the lambda/functor in-place
97 new (bytes) Function(fl::move(f));
98
99 // Set up type-erased function pointers
100 invoker = &invoke_lambda<Function>;
101 destructor = &destroy_lambda<Function>;
102 }
103
104 // Copy constructor
105 InlinedLambda(const InlinedLambda& other)
106 : invoker(other.invoker), destructor(other.destructor) {
107 // This is tricky - we need to copy the stored object
108 // For now, we'll use memcopy (works for trivially copyable types)
109 fl::memcopy(bytes, other.bytes, kInlineLambdaSize);
110 }
111
112 // Move constructor
113 InlinedLambda(InlinedLambda&& other)
114 : invoker(other.invoker), destructor(other.destructor) {
115 fl::memcopy(bytes, other.bytes, kInlineLambdaSize);
116 // Reset the other object to prevent double destruction
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 // Use placement new to safely access the stored lambda
129 alignas(FUNCTOR) char temp_storage[sizeof(FUNCTOR)];
130 // Copy the lambda from storage
131 fl::memcopy(temp_storage, storage.bytes, sizeof(FUNCTOR));
132 // Get a properly typed pointer to the copied lambda (non-const for mutable lambdas)
133 FUNCTOR* f = static_cast<FUNCTOR*>(static_cast<void*>(temp_storage));
134 // Invoke the lambda
135 return (*f)(args...);
136 }
137
138 template <typename FUNCTOR>
139 static void destroy_lambda(InlinedLambda& storage) {
140 // For destruction, we need to call the destructor on the actual object
141 // that was constructed with placement new in storage.bytes
142 // We use the standard library approach: create a properly typed pointer
143 // using placement new, then call the destructor through that pointer
144
145 // This is the standard-compliant way to get a properly typed pointer
146 // to an object that was constructed with placement new
147 FUNCTOR* obj_ptr = static_cast<FUNCTOR*>(static_cast<void*>(storage.bytes));
148 obj_ptr->~FUNCTOR();
149 }
150
151 R invoke(Args... args) const {
152 return invoker(*this, args...);
153 }
154 };
155
156 // Type-erased member function callable base
157 struct MemberCallableBase {
158 virtual R invoke(Args... args) const = 0;
159 virtual ~MemberCallableBase() = default;
160 };
161
162 // Type-erased non-const member function callable
163 struct NonConstMemberCallable : MemberCallableBase {
164 void* obj;
165 // Union to store member function pointer as raw bytes
166 union MemberFuncStorage {
167 char bytes[sizeof(R (NonConstMemberCallable::*)(Args...))];
168 // Ensure proper alignment
169 void* alignment_dummy;
170 } member_func_storage;
171
172 // Type-erased invoker function - set at construction time
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 // Store the member function pointer as raw bytes
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 // Set the invoker to a function that knows how to cast back and call
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
193 R invoke(Args... args) const override {
194 return invoker(obj, member_func_storage, args...);
195 }
196 };
197
198 // Type-erased const member function callable
199 struct ConstMemberCallable : MemberCallableBase {
200 const void* obj;
201 // Union to store member function pointer as raw bytes
202 union MemberFuncStorage {
203 char bytes[sizeof(R (ConstMemberCallable::*)(Args...) const)];
204 // Ensure proper alignment
205 void* alignment_dummy;
206 } member_func_storage;
207
208 // Type-erased invoker function - set at construction time
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 // Store the member function pointer as raw bytes
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 // Set the invoker to a function that knows how to cast back and call
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
229 R invoke(Args... args) const override {
230 return invoker(obj, member_func_storage, args...);
231 }
232 };
233
234 // Variant to store any of our callable types inline (with heap fallback for large lambdas)
235 using Storage = Variant<fl::shared_ptr<CallableBase>, FreeFunctionCallable, InlinedLambda, NonConstMemberCallable, ConstMemberCallable>;
236 Storage storage_;
237
238 // Helper function to handle default return value for void and non-void types
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 // Copy constructor - properly handle Variant alignment
255 function(const function& other) : storage_(other.storage_) {}
256
257 // Move constructor - properly handle Variant alignment
258 function(function&& other) noexcept : storage_(fl::move(other.storage_)) {}
259
260 // Copy assignment
261 function& operator=(const function& other) {
262 if (this != &other) {
263 storage_ = other.storage_;
264 }
265 return *this;
266 }
267
268 // Move assignment
269 function& operator=(function&& other) noexcept {
270 if (this != &other) {
271 storage_ = fl::move(other.storage_);
272 }
273 return *this;
274 }
275
276 // 1) Free function constructor - stored inline!
277 function(R (*fp)(Args...)) {
278 storage_ = FreeFunctionCallable(fp);
279 }
280
281 // 2) Lambda/functor constructor - inline if small, heap if large
282 template <typename F, typename = enable_if_t<!is_member_function_pointer<F>::value && !is_function_pointer<F>::value>>
283 function(F f) {
284 // Use template specialization instead of if constexpr for C++14 compatibility
285 construct_lambda_or_functor(fl::move(f), typename conditional<sizeof(F) <= kInlineLambdaSize, true_type, false_type>::type{});
286 }
287
288 // 3) non‑const member function - stored inline!
289 template <typename C>
290 function(R (C::*mf)(Args...), C* obj) {
291 storage_ = NonConstMemberCallable(obj, mf);
292 }
293
294 // 4) const member function - stored inline!
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 // Direct dispatch using type checking - efficient and simple
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 // This should never happen if the function is properly constructed
314 return default_return_helper<R>();
315 }
316
317 explicit operator bool() const {
318 return !storage_.empty();
319 }
320
321 void clear() {
322 storage_ = Storage{}; // Reset to empty variant
323 }
324
325 bool operator==(const function& o) const {
326 // For simplicity, just check if both are empty or both are non-empty
327 // Full equality would require more complex comparison logic
328 return storage_.empty() == o.storage_.empty();
329 }
330
331 bool operator!=(const function& o) const {
332 return !(*this == o);
333 }
334
335private:
336 // Helper for small lambdas/functors - inline storage
337 template <typename F>
338 void construct_lambda_or_functor(F f, true_type /* small */) {
339 storage_ = InlinedLambda(fl::move(f));
340 }
341
342 // Helper for large lambdas/functors - heap storage
343 template <typename F>
344 void construct_lambda_or_functor(F f, false_type /* large */) {
345 storage_ = fl::shared_ptr<CallableBase>(fl::make_shared<Callable<F>>(fl::move(f)));
346 }
347};
348
349} // namespace fl
#define FL_ALIGN
Definition align.h:16
FASTLED_FORCE_INLINE bool operator!=(const CRGB &lhs, const CRGB &rhs)
Check if two CRGB objects do not have the same color data.
Definition crgb.h:797
FASTLED_FORCE_INLINE bool operator==(const CRGB &lhs, const CRGB &rhs)
Check if two CRGB objects have the same color data.
Definition crgb.h:791
#define FASTLED_INLINE_LAMBDA_SIZE
Definition function.h:13
constexpr remove_reference< T >::type && move(T &&t) noexcept
Definition move.h:27
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)...))>
Definition functional.h:49
void * memcopy(void *dst, const void *src, fl::size num)
Definition memfill.h:30
void clear(CRGB(&arr)[N])
Definition clear.h:13
void * memfill(void *ptr, int value, fl::size num)
Definition memfill.h:11
shared_ptr< T > make_shared(Args &&... args)
Definition shared_ptr.h:348
corkscrew_args args
Definition old.h:150

References fl::Transform16::Transform16(), ALMOST_EQUAL_DOUBLE, ALMOST_EQUAL_FLOAT, args, fl::clamp(), fl::TransformFloat::compile(), cos16(), FASTLED_FORCE_INLINE, FASTLED_INLINE_LAMBDA_SIZE, FL_ALIGN, FL_DISABLE_WARNING_FLOAT_CONVERSION, FL_DISABLE_WARNING_IMPLICIT_INT_CONVERSION, FL_DISABLE_WARNING_SIGN_CONVERSION, fl::TransformFloatImpl::is_identity(), fl::Matrix3x3f::m, fl::make_shared(), map32_to_16(), fl::UISlider::mCallbacks, fl::memcopy(), fl::memfill(), fl::UISlider::mImpl, MIN, fl::UISlider::mLastFrameValue, fl::UISlider::mLastFramevalueValid, fl::move(), fl::UISlider::Listener::mOwner, fl::Transform16::offset_x, fl::TransformFloat::offset_x(), fl::TransformFloatImpl::offset_x, fl::Transform16::offset_y, fl::TransformFloat::offset_y(), fl::TransformFloatImpl::offset_y, fl::UIButton::Listener::onBeginFrame(), fl::UICheckbox::Listener::onBeginFrame(), fl::UIDropdown::Listener::onBeginFrame(), fl::UINumberField::Listener::onBeginFrame(), fl::UISlider::Listener::onBeginFrame(), operator!=(), operator==(), PI, fl::Transform16::rotation, fl::TransformFloat::rotation(), fl::TransformFloatImpl::rotation, fl::TransformFloatImpl::scale(), scale, scale16(), fl::TransformFloat::scale_x(), scale_x, fl::TransformFloat::scale_y(), scale_y, fl::TransformFloatImpl::set_scale(), fl::UISliderImpl::setValue(), sin16, fl::Transform16::ToBounds(), fl::Transform16::transform(), fl::TransformFloatImpl::transform(), fl::UISlider::value, fl::UISliderImpl::value(), x, xy(), and y.

+ Here is the call graph for this function: