| 335 | #endif // FL_FUNCTION_FULL_VARIANT |
| 336 | |
| 337 | R operator()(Args... args) const FL_NOEXCEPT { |
| 338 | // Direct dispatch using type checking - efficient and simple |
| 339 | #if FL_FUNCTION_FULL_VARIANT |
| 340 | if (auto* heap_callable = mStorage.template ptr<fl::shared_ptr<CallableBase>>()) { |
| 341 | return (*heap_callable)->invoke(args...); |
| 342 | } else if (auto* free_func = mStorage.template ptr<FreeFunctionCallable>()) { |
| 343 | #else |
| 344 | if (auto* free_func = mStorage.template ptr<FreeFunctionCallable>()) { |
| 345 | #endif |
| 346 | return free_func->invoke(args...); |
| 347 | } else if (auto* inlined_lambda = mStorage.template ptr<InlinedLambda>()) { |
| 348 | return inlined_lambda->invoke(args...); |
| 349 | #if FL_FUNCTION_FULL_VARIANT |
| 350 | } else if (auto* nonconst_member = mStorage.template ptr<NonConstMemberCallable>()) { |
| 351 | return nonconst_member->invoke(args...); |
| 352 | } else if (auto* const_member = mStorage.template ptr<ConstMemberCallable>()) { |
| 353 | return const_member->invoke(args...); |
| 354 | #endif |
| 355 | } |
| 356 | // This should never happen if the function is properly constructed |
| 357 | return default_return_helper<R>(); |
| 358 | } |
| 359 | |
| 360 | explicit operator bool() const FL_NOEXCEPT { |
| 361 | return !mStorage.empty(); |
| 362 | } |
| 363 | |
| 364 | void clear() FL_NOEXCEPT { |
| 365 | mStorage = Storage{}; // Reset to empty variant |
| 366 | } |
| 367 | |
| 368 | bool operator==(const function& o) const FL_NOEXCEPT { |
| 369 | // For simplicity, just check if both are empty or both are non-empty |
| 370 | // Full equality would require more complex comparison logic |
| 371 | return mStorage.empty() == o.mStorage.empty(); |
| 372 | } |
| 373 | |
| 374 | bool operator!=(const function& o) const FL_NOEXCEPT { |
| 375 | return !(*this == o); |
| 376 | } |
| 377 | |
| 378 | private: |
| 379 | // Helper for small lambdas/functors - inline storage |
| 380 | template <typename F> |
| 381 | void construct_lambda_or_functor(F f, true_type /* small */) FL_NOEXCEPT { |
| 382 | mStorage = InlinedLambda(fl::move(f)); |
| 383 | } |
| 384 | |
| 385 | #if FL_FUNCTION_FULL_VARIANT |
| 386 | // Helper for large lambdas/functors - heap storage |
| 387 | template <typename F> |
| 388 | void construct_lambda_or_functor(F f, false_type /* large */) FL_NOEXCEPT { |
| 389 | mStorage = fl::shared_ptr<CallableBase>(fl::make_shared<Callable<F>>(fl::move(f))); |
| 390 | } |
| 391 | #else |
| 392 | // Low-memory targets: heap fallback dropped per FastLED #3077 to save flash. |
| 393 | // Large lambdas (>kInlineLambdaSize) compile but leave the storage empty; |
| 394 | // operator() returns the default-constructed R. This degrades gracefully |
nothing calls this directly
no test coverage detected