v1.2版本改为通过xbox系列手柄进行遥控,并增加智能模式和手动模式
This commit is contained in:
1 parent
76603d3c31
commit
1a97bc32c3
1839 files changed
+478852
-3
No files matched your search
@@ -0,0 +1,351 @@
|
||||
#pragma once
|
||||
#include "fl/memory.h"
|
||||
#include "fl/type_traits.h"
|
||||
#include "fl/compiler_control.h"
|
||||
#include "fl/variant.h"
|
||||
#include "fl/memfill.h"
|
||||
#include "fl/type_traits.h"
|
||||
#include "fl/inplacenew.h"
|
||||
#include "fl/bit_cast.h"
|
||||
#include "fl/align.h"
|
||||
|
||||
#ifndef FASTLED_INLINE_LAMBDA_SIZE
|
||||
#define FASTLED_INLINE_LAMBDA_SIZE 64
|
||||
#endif
|
||||
|
||||
FL_DISABLE_WARNING_PUSH
|
||||
FL_DISABLE_WARNING(float-equal)
|
||||
|
||||
namespace fl {
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// is_function_pointer trait - detects function pointers like R(*)(Args...)
|
||||
//----------------------------------------------------------------------------
|
||||
template <typename T> struct is_function_pointer {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct is_function_pointer<R(*)(Args...)> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// More or less a drop in replacement for std::function
|
||||
// function<R(Args...)>: type‐erasing "std::function" replacement
|
||||
// Supports free functions, lambdas/functors, member functions (const &
|
||||
// non‑const)
|
||||
//
|
||||
// NEW: Uses inline storage for member functions, free functions, and small
|
||||
// lambdas/functors. Only large lambdas/functors use heap allocation.
|
||||
//----------------------------------------------------------------------------
|
||||
template <typename> class function;
|
||||
|
||||
|
||||
|
||||
template <typename R, typename... Args>
|
||||
class FL_ALIGN function<R(Args...)> {
|
||||
private:
|
||||
struct CallableBase {
|
||||
virtual R invoke(Args... args) = 0;
|
||||
virtual ~CallableBase() = default;
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
struct Callable : CallableBase {
|
||||
F f;
|
||||
Callable(F fn) : f(fn) {}
|
||||
R invoke(Args... args) override { return f(args...); }
|
||||
};
|
||||
|
||||
// Type-erased free function callable - stored inline!
|
||||
struct FreeFunctionCallable {
|
||||
R (*func_ptr)(Args...);
|
||||
|
||||
FreeFunctionCallable(R (*fp)(Args...)) : func_ptr(fp) {}
|
||||
|
||||
R invoke(Args... args) const {
|
||||
return func_ptr(args...);
|
||||
}
|
||||
};
|
||||
|
||||
// Type-erased small lambda/functor callable - stored inline!
|
||||
// Size limit for inline storage - configurable via preprocessor define
|
||||
|
||||
static constexpr fl::size kInlineLambdaSize = FASTLED_INLINE_LAMBDA_SIZE;
|
||||
|
||||
struct InlinedLambda {
|
||||
// Storage for the lambda/functor object
|
||||
// Use aligned storage to ensure proper alignment for any type
|
||||
alignas(max_align_t) char bytes[kInlineLambdaSize];
|
||||
|
||||
// Type-erased invoker and destructor function pointers
|
||||
R (*invoker)(const InlinedLambda& storage, Args... args);
|
||||
void (*destructor)(InlinedLambda& storage);
|
||||
|
||||
template <typename Function>
|
||||
InlinedLambda(Function f) {
|
||||
static_assert(sizeof(Function) <= kInlineLambdaSize,
|
||||
"Lambda/functor too large for inline storage");
|
||||
static_assert(alignof(Function) <= alignof(max_align_t),
|
||||
"Lambda/functor requires stricter alignment than storage provides");
|
||||
|
||||
// Initialize the entire storage to zero to avoid copying uninitialized memory
|
||||
fl::memfill(bytes, 0, kInlineLambdaSize);
|
||||
|
||||
// Construct the lambda/functor in-place
|
||||
new (bytes) Function(fl::move(f));
|
||||
|
||||
// Set up type-erased function pointers
|
||||
invoker = &invoke_lambda<Function>;
|
||||
destructor = &destroy_lambda<Function>;
|
||||
}
|
||||
|
||||
// Copy constructor
|
||||
InlinedLambda(const InlinedLambda& other)
|
||||
: invoker(other.invoker), destructor(other.destructor) {
|
||||
// This is tricky - we need to copy the stored object
|
||||
// For now, we'll use memcopy (works for trivially copyable types)
|
||||
fl::memcopy(bytes, other.bytes, kInlineLambdaSize);
|
||||
}
|
||||
|
||||
// Move constructor
|
||||
InlinedLambda(InlinedLambda&& other)
|
||||
: invoker(other.invoker), destructor(other.destructor) {
|
||||
fl::memcopy(bytes, other.bytes, kInlineLambdaSize);
|
||||
// Reset the other object to prevent double destruction
|
||||
other.destructor = nullptr;
|
||||
}
|
||||
|
||||
~InlinedLambda() {
|
||||
if (destructor) {
|
||||
destructor(*this);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FUNCTOR>
|
||||
static R invoke_lambda(const InlinedLambda& storage, Args... args) {
|
||||
// Use placement new to safely access the stored lambda
|
||||
alignas(FUNCTOR) char temp_storage[sizeof(FUNCTOR)];
|
||||
// Copy the lambda from storage
|
||||
fl::memcopy(temp_storage, storage.bytes, sizeof(FUNCTOR));
|
||||
// Get a properly typed pointer to the copied lambda (non-const for mutable lambdas)
|
||||
FUNCTOR* f = static_cast<FUNCTOR*>(static_cast<void*>(temp_storage));
|
||||
// Invoke the lambda
|
||||
return (*f)(args...);
|
||||
}
|
||||
|
||||
template <typename FUNCTOR>
|
||||
static void destroy_lambda(InlinedLambda& storage) {
|
||||
// For destruction, we need to call the destructor on the actual object
|
||||
// that was constructed with placement new in storage.bytes
|
||||
// We use the standard library approach: create a properly typed pointer
|
||||
// using placement new, then call the destructor through that pointer
|
||||
|
||||
// This is the standard-compliant way to get a properly typed pointer
|
||||
// to an object that was constructed with placement new
|
||||
FUNCTOR* obj_ptr = static_cast<FUNCTOR*>(static_cast<void*>(storage.bytes));
|
||||
obj_ptr->~FUNCTOR();
|
||||
}
|
||||
|
||||
R invoke(Args... args) const {
|
||||
return invoker(*this, args...);
|
||||
}
|
||||
};
|
||||
|
||||
// Type-erased member function callable base
|
||||
struct MemberCallableBase {
|
||||
virtual R invoke(Args... args) const = 0;
|
||||
virtual ~MemberCallableBase() = default;
|
||||
};
|
||||
|
||||
// Type-erased non-const member function callable
|
||||
struct NonConstMemberCallable : MemberCallableBase {
|
||||
void* obj;
|
||||
// Union to store member function pointer as raw bytes
|
||||
union MemberFuncStorage {
|
||||
char bytes[sizeof(R (NonConstMemberCallable::*)(Args...))];
|
||||
// Ensure proper alignment
|
||||
void* alignment_dummy;
|
||||
} member_func_storage;
|
||||
|
||||
// Type-erased invoker function - set at construction time
|
||||
R (*invoker)(void* obj, const MemberFuncStorage& mfp, Args... args);
|
||||
|
||||
template <typename C>
|
||||
NonConstMemberCallable(C* o, R (C::*mf)(Args...)) : obj(o) {
|
||||
// Store the member function pointer as raw bytes
|
||||
static_assert(sizeof(mf) <= sizeof(member_func_storage),
|
||||
"Member function pointer too large");
|
||||
fl::memcopy(member_func_storage.bytes, &mf, sizeof(mf));
|
||||
// Set the invoker to a function that knows how to cast back and call
|
||||
invoker = &invoke_nonconst_member<C>;
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
static R invoke_nonconst_member(void* obj, const MemberFuncStorage& mfp, Args... args) {
|
||||
C* typed_obj = static_cast<C*>(obj);
|
||||
R (C::*typed_mf)(Args...);
|
||||
fl::memcopy(&typed_mf, mfp.bytes, sizeof(typed_mf));
|
||||
return (typed_obj->*typed_mf)(args...);
|
||||
}
|
||||
|
||||
R invoke(Args... args) const override {
|
||||
return invoker(obj, member_func_storage, args...);
|
||||
}
|
||||
};
|
||||
|
||||
// Type-erased const member function callable
|
||||
struct ConstMemberCallable : MemberCallableBase {
|
||||
const void* obj;
|
||||
// Union to store member function pointer as raw bytes
|
||||
union MemberFuncStorage {
|
||||
char bytes[sizeof(R (ConstMemberCallable::*)(Args...) const)];
|
||||
// Ensure proper alignment
|
||||
void* alignment_dummy;
|
||||
} member_func_storage;
|
||||
|
||||
// Type-erased invoker function - set at construction time
|
||||
R (*invoker)(const void* obj, const MemberFuncStorage& mfp, Args... args);
|
||||
|
||||
template <typename C>
|
||||
ConstMemberCallable(const C* o, R (C::*mf)(Args...) const) : obj(o) {
|
||||
// Store the member function pointer as raw bytes
|
||||
static_assert(sizeof(mf) <= sizeof(member_func_storage),
|
||||
"Member function pointer too large");
|
||||
fl::memcopy(member_func_storage.bytes, &mf, sizeof(mf));
|
||||
// Set the invoker to a function that knows how to cast back and call
|
||||
invoker = &invoke_const_member<C>;
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
static R invoke_const_member(const void* obj, const MemberFuncStorage& mfp, Args... args) {
|
||||
const C* typed_obj = static_cast<const C*>(obj);
|
||||
R (C::*typed_mf)(Args...) const;
|
||||
fl::memcopy(&typed_mf, mfp.bytes, sizeof(typed_mf));
|
||||
return (typed_obj->*typed_mf)(args...);
|
||||
}
|
||||
|
||||
R invoke(Args... args) const override {
|
||||
return invoker(obj, member_func_storage, args...);
|
||||
}
|
||||
};
|
||||
|
||||
// Variant to store any of our callable types inline (with heap fallback for large lambdas)
|
||||
using Storage = Variant<fl::shared_ptr<CallableBase>, FreeFunctionCallable, InlinedLambda, NonConstMemberCallable, ConstMemberCallable>;
|
||||
Storage storage_;
|
||||
|
||||
// Helper function to handle default return value for void and non-void types
|
||||
template<typename ReturnType>
|
||||
typename enable_if<!is_void<ReturnType>::value, ReturnType>::type
|
||||
default_return_helper() const {
|
||||
return ReturnType{};
|
||||
}
|
||||
|
||||
template<typename ReturnType>
|
||||
typename enable_if<is_void<ReturnType>::value, ReturnType>::type
|
||||
default_return_helper() const {
|
||||
return;
|
||||
}
|
||||
|
||||
public:
|
||||
function() = default;
|
||||
|
||||
// Copy constructor - properly handle Variant alignment
|
||||
function(const function& other) : storage_(other.storage_) {}
|
||||
|
||||
// Move constructor - properly handle Variant alignment
|
||||
function(function&& other) noexcept : storage_(fl::move(other.storage_)) {}
|
||||
|
||||
// Copy assignment
|
||||
function& operator=(const function& other) {
|
||||
if (this != &other) {
|
||||
storage_ = other.storage_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Move assignment
|
||||
function& operator=(function&& other) noexcept {
|
||||
if (this != &other) {
|
||||
storage_ = fl::move(other.storage_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// 1) Free function constructor - stored inline!
|
||||
function(R (*fp)(Args...)) {
|
||||
storage_ = FreeFunctionCallable(fp);
|
||||
}
|
||||
|
||||
// 2) Lambda/functor constructor - inline if small, heap if large
|
||||
template <typename F, typename = enable_if_t<!is_member_function_pointer<F>::value && !is_function_pointer<F>::value>>
|
||||
function(F f) {
|
||||
// Use template specialization instead of if constexpr for C++14 compatibility
|
||||
construct_lambda_or_functor(fl::move(f), typename conditional<sizeof(F) <= kInlineLambdaSize, true_type, false_type>::type{});
|
||||
}
|
||||
|
||||
// 3) non‑const member function - stored inline!
|
||||
template <typename C>
|
||||
function(R (C::*mf)(Args...), C* obj) {
|
||||
storage_ = NonConstMemberCallable(obj, mf);
|
||||
}
|
||||
|
||||
// 4) const member function - stored inline!
|
||||
template <typename C>
|
||||
function(R (C::*mf)(Args...) const, const C* obj) {
|
||||
storage_ = ConstMemberCallable(obj, mf);
|
||||
}
|
||||
|
||||
R operator()(Args... args) const {
|
||||
// Direct dispatch using type checking - efficient and simple
|
||||
if (auto* heap_callable = storage_.template ptr<fl::shared_ptr<CallableBase>>()) {
|
||||
return (*heap_callable)->invoke(args...);
|
||||
} else if (auto* free_func = storage_.template ptr<FreeFunctionCallable>()) {
|
||||
return free_func->invoke(args...);
|
||||
} else if (auto* inlined_lambda = storage_.template ptr<InlinedLambda>()) {
|
||||
return inlined_lambda->invoke(args...);
|
||||
} else if (auto* nonconst_member = storage_.template ptr<NonConstMemberCallable>()) {
|
||||
return nonconst_member->invoke(args...);
|
||||
} else if (auto* const_member = storage_.template ptr<ConstMemberCallable>()) {
|
||||
return const_member->invoke(args...);
|
||||
}
|
||||
// This should never happen if the function is properly constructed
|
||||
return default_return_helper<R>();
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return !storage_.empty();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
storage_ = Storage{}; // Reset to empty variant
|
||||
}
|
||||
|
||||
bool operator==(const function& o) const {
|
||||
// For simplicity, just check if both are empty or both are non-empty
|
||||
// Full equality would require more complex comparison logic
|
||||
return storage_.empty() == o.storage_.empty();
|
||||
}
|
||||
|
||||
bool operator!=(const function& o) const {
|
||||
return !(*this == o);
|
||||
}
|
||||
|
||||
private:
|
||||
// Helper for small lambdas/functors - inline storage
|
||||
template <typename F>
|
||||
void construct_lambda_or_functor(F f, true_type /* small */) {
|
||||
storage_ = InlinedLambda(fl::move(f));
|
||||
}
|
||||
|
||||
// Helper for large lambdas/functors - heap storage
|
||||
template <typename F>
|
||||
void construct_lambda_or_functor(F f, false_type /* large */) {
|
||||
storage_ = fl::shared_ptr<CallableBase>(fl::make_shared<Callable<F>>(fl::move(f)));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
|
||||
FL_DISABLE_WARNING_POP
|
||||
Reference in new issue
Block a user