/* Delegate.h - An efficient interchangeable C function ptr and C++ std::function delegate Copyright (c) 2019 Dirk O. Kaar. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __Delegate_h #define __Delegate_h #if defined(ESP8266) #include #elif defined(ESP32) #include #else #define IRAM_ATTR #endif #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) #include #include #else #include "circular_queue/ghostl.h" #endif namespace { template __attribute__((always_inline)) inline R IRAM_ATTR vPtrToFunPtrExec(void* fn, P... args) { using target_type = R(P...); return reinterpret_cast(fn)(std::forward(args...)); } } namespace delegate { namespace detail { #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) template class DelegatePImpl { public: using target_type = R(P...); protected: using FunPtr = target_type*; using FunAPtr = R(*)(A, P...); using FunVPPtr = R(*)(void*, P...); using FunctionType = std::function; public: DelegatePImpl() { kind = FP; fn = nullptr; } DelegatePImpl(std::nullptr_t) { kind = FP; fn = nullptr; } ~DelegatePImpl() { if (FUNC == kind) functional.~FunctionType(); else if (FPA == kind) obj.~A(); } DelegatePImpl(const DelegatePImpl& del) { kind = del.kind; if (FUNC == del.kind) { new (&functional) FunctionType(del.functional); } else if (FPA == del.kind) { fnA = del.fnA; new (&obj) A(del.obj); } else { fn = del.fn; } } DelegatePImpl(DelegatePImpl&& del) { kind = del.kind; if (FUNC == del.kind) { new (&functional) FunctionType(std::move(del.functional)); } else if (FPA == del.kind) { fnA = del.fnA; new (&obj) A(std::move(del.obj)); } else { fn = del.fn; } } DelegatePImpl(FunAPtr fnA, const A& obj) { kind = FPA; DelegatePImpl::fnA = fnA; new (&this->obj) A(obj); } DelegatePImpl(FunAPtr fnA, A&& obj) { kind = FPA; DelegatePImpl::fnA = fnA; new (&this->obj) A(std::move(obj)); } DelegatePImpl(FunPtr fn) { kind = FP; DelegatePImpl::fn = fn; } template DelegatePImpl(F functional) { kind = FUNC; new (&this->functional) FunctionType(std::forward(functional)); } DelegatePImpl& operator=(const DelegatePImpl& del) { if (this == &del) return *this; if (kind != del.kind) { if (FUNC == kind) { functional.~FunctionType(); } else if (FPA == kind) { obj.~A(); } if (FUNC == del.kind) { new (&this->functional) FunctionType(); } else if (FPA == del.kind) { new (&obj) A; } kind = del.kind; } if (FUNC == del.kind) { functional = del.functional; } else if (FPA == del.kind) { fnA = del.fnA; obj = del.obj; } else { fn = del.fn; } return *this; } DelegatePImpl& operator=(DelegatePImpl&& del) { if (this == &del) return *this; if (kind != del.kind) { if (FUNC == kind) { functional.~FunctionType(); } else if (FPA == kind) { obj.~A(); } if (FUNC == del.kind) { new (&this->functional) FunctionType(); } else if (FPA == del.kind) { new (&obj) A; } kind = del.kind; } if (FUNC == del.kind) { functional = std::move(del.functional); } else if (FPA == del.kind) { fnA = del.fnA; obj = std::move(del.obj); } else { fn = del.fn; } return *this; } DelegatePImpl& operator=(FunPtr fn) { if (FUNC == kind) { functional.~FunctionType(); } else if (FPA == kind) { obj.~A(); } kind = FP; this->fn = fn; return *this; } DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) { if (FUNC == kind) { functional.~FunctionType(); } else if (FPA == kind) { obj.~A(); } kind = FP; fn = nullptr; return *this; } IRAM_ATTR operator bool() const { if (FP == kind) { return fn; } else if (FPA == kind) { return fnA; } else { return functional ? true : false; } } static inline R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) __attribute__((always_inline)) { return static_cast(self)->fnA( static_cast(self)->obj, std::forward(args...)); }; operator FunVPPtr() const { if (FP == kind) { return vPtrToFunPtrExec; } else if (FPA == kind) { return vPtrToFunAPtrExec; } else { return [](void* self, P... args) -> R { return static_cast(self)->functional(std::forward(args...)); }; } } void* arg() const { if (FP == kind) { return reinterpret_cast(fn); } else { return const_cast(this); } } operator FunctionType() const { if (FP == kind) { return fn; } else if (FPA == kind) { return [this](P... args) { return fnA(obj, std::forward(args...)); }; } else { return functional; } } /// Calling is safe without checking for nullptr. /// If non-void, returns the default value. /// In ISR context, where faults and exceptions must not /// occurs, this saves the extra check for nullptr, /// and allows the compiler to optimize out checks /// in std::function which may not be ISR-safe or /// cause linker errors, like l32r relocation errors /// on the Xtensa ISA. R IRAM_ATTR operator()(P... args) const { if (FP == kind) { if (fn) return fn(std::forward(args...)); } else if (FPA == kind) { if (fnA) return fnA(obj, std::forward(args...)); } else { if (functional) return functional(std::forward(args...)); } return R(); } protected: union { FunctionType functional; FunPtr fn; struct { FunAPtr fnA; A obj; }; }; enum { FUNC, FP, FPA } kind; }; #else template class DelegatePImpl { public: using target_type = R(P...); protected: using FunPtr = target_type*; using FunAPtr = R(*)(A, P...); using FunVPPtr = R(*)(void*, P...); public: DelegatePImpl() { kind = FP; fn = nullptr; } DelegatePImpl(std::nullptr_t) { kind = FP; fn = nullptr; } DelegatePImpl(const DelegatePImpl& del) { kind = del.kind; if (FPA == del.kind) { fnA = del.fnA; obj = del.obj; } else { fn = del.fn; } } DelegatePImpl(DelegatePImpl&& del) { kind = del.kind; if (FPA == del.kind) { fnA = del.fnA; obj = std::move(del.obj); } else { fn = del.fn; } } DelegatePImpl(FunAPtr fnA, const A& obj) { kind = FPA; DelegatePImpl::fnA = fnA; this->obj = obj; } DelegatePImpl(FunAPtr fnA, A&& obj) { kind = FPA; DelegatePImpl::fnA = fnA; this->obj = std::move(obj); } DelegatePImpl(FunPtr fn) { kind = FP; DelegatePImpl::fn = fn; } template DelegatePImpl(F functional) { kind = FP; fn = std::forward(functional); } DelegatePImpl& operator=(const DelegatePImpl& del) { if (this == &del) return *this; if (kind != del.kind) { if (FPA == kind) { obj = {}; } kind = del.kind; } if (FPA == del.kind) { fnA = del.fnA; obj = del.obj; } else { fn = del.fn; } return *this; } DelegatePImpl& operator=(DelegatePImpl&& del) { if (this == &del) return *this; if (kind != del.kind) { if (FPA == kind) { obj = {}; } kind = del.kind; } if (FPA == del.kind) { fnA = del.fnA; obj = std::move(del.obj); } else { fn = del.fn; } return *this; } DelegatePImpl& operator=(FunPtr fn) { if (FPA == kind) { obj = {}; } kind = FP; this->fn = fn; return *this; } DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) { if (FPA == kind) { obj = {}; } kind = FP; fn = nullptr; return *this; } IRAM_ATTR operator bool() const { if (FP == kind) { return fn; } else { return fnA; } } static inline R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) __attribute__((always_inline)) { return static_cast(self)->fnA( static_cast(self)->obj, std::forward(args...)); }; operator FunVPPtr() const { if (FP == kind) { return vPtrToFunPtrExec; } else { return vPtrToFunAPtrExec; } } void* arg() const { if (FP == kind) { return reinterpret_cast(fn); } else { return const_cast(this); } } /// Calling is safe without checking for nullptr. /// If non-void, returns the default value. /// In ISR context, where faults and exceptions must not /// occurs, this saves the extra check for nullptr, /// and allows the compiler to optimize out checks /// in std::function which may not be ISR-safe or /// cause linker errors, like l32r relocation errors /// on the Xtensa ISA. R IRAM_ATTR operator()(P... args) const { if (FP == kind) { if (fn) return fn(std::forward(args...)); } else { if (fnA) return fnA(obj, std::forward(args...)); } return R(); } protected: union { FunPtr fn; FunAPtr fnA; }; A obj; enum { FP, FPA } kind; }; #endif #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) template class DelegatePImpl { public: using target_type = R(P...); protected: using FunPtr = target_type*; using FunctionType = std::function; using FunVPPtr = R(*)(void*, P...); public: DelegatePImpl() { kind = FP; fn = nullptr; } DelegatePImpl(std::nullptr_t) { kind = FP; fn = nullptr; } ~DelegatePImpl() { if (FUNC == kind) functional.~FunctionType(); } DelegatePImpl(const DelegatePImpl& del) { kind = del.kind; if (FUNC == del.kind) { new (&functional) FunctionType(del.functional); } else { fn = del.fn; } } DelegatePImpl(DelegatePImpl&& del) { kind = del.kind; if (FUNC == del.kind) { new (&functional) FunctionType(std::move(del.functional)); } else { fn = del.fn; } } DelegatePImpl(FunPtr fn) { kind = FP; DelegatePImpl::fn = fn; } template DelegatePImpl(F functional) { kind = FUNC; new (&this->functional) FunctionType(std::forward(functional)); } DelegatePImpl& operator=(const DelegatePImpl& del) { if (this == &del) return *this; if (FUNC == kind && FUNC != del.kind) { functional.~FunctionType(); } else if (FUNC != kind && FUNC == del.kind) { new (&this->functional) FunctionType(); } kind = del.kind; if (FUNC == del.kind) { functional = del.functional; } else { fn = del.fn; } return *this; } DelegatePImpl& operator=(DelegatePImpl&& del) { if (this == &del) return *this; if (FUNC == kind && FUNC != del.kind) { functional.~FunctionType(); } else if (FUNC != kind && FUNC == del.kind) { new (&this->functional) FunctionType(); } kind = del.kind; if (FUNC == del.kind) { functional = std::move(del.functional); } else { fn = del.fn; } return *this; } DelegatePImpl& operator=(FunPtr fn) { if (FUNC == kind) { functional.~FunctionType(); kind = FP; } DelegatePImpl::fn = fn; return *this; } DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) { if (FUNC == kind) { functional.~FunctionType(); } kind = FP; fn = nullptr; return *this; } IRAM_ATTR operator bool() const { if (FP == kind) { return fn; } else { return functional ? true : false; } } operator FunVPPtr() const { if (FP == kind) { return vPtrToFunPtrExec; } else { return [](void* self, P... args) -> R { return static_cast(self)->functional(std::forward(args...)); }; } } void* arg() const { if (FP == kind) { return reinterpret_cast(fn); } else { return const_cast(this); } } operator FunctionType() const { if (FP == kind) { return fn; } else { return functional; } } /// Calling is safe without checking for nullptr. /// If non-void, returns the default value. /// In ISR context, where faults and exceptions must not /// occurs, this saves the extra check for nullptr, /// and allows the compiler to optimize out checks /// in std::function which may not be ISR-safe or /// cause linker errors, like l32r relocation errors /// on the Xtensa ISA. R IRAM_ATTR operator()(P... args) const { if (FP == kind) { if (fn) return fn(std::forward(args...)); } else { if (functional) return functional(std::forward(args...)); } return R(); } protected: union { FunctionType functional; FunPtr fn; }; enum { FUNC, FP } kind; }; #else template class DelegatePImpl { public: using target_type = R(P...); protected: using FunPtr = target_type*; using FunVPPtr = R(*)(void*, P...); public: DelegatePImpl() { fn = nullptr; } DelegatePImpl(std::nullptr_t) { fn = nullptr; } DelegatePImpl(const DelegatePImpl& del) { fn = del.fn; } DelegatePImpl(DelegatePImpl&& del) { fn = std::move(del.fn); } DelegatePImpl(FunPtr fn) { DelegatePImpl::fn = fn; } template DelegatePImpl(F fn) { DelegatePImpl::fn = std::forward(fn); } DelegatePImpl& operator=(const DelegatePImpl& del) { if (this == &del) return *this; fn = del.fn; return *this; } DelegatePImpl& operator=(DelegatePImpl&& del) { if (this == &del) return *this; fn = std::move(del.fn); return *this; } DelegatePImpl& operator=(FunPtr fn) { DelegatePImpl::fn = fn; return *this; } inline DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { fn = nullptr; return *this; } inline IRAM_ATTR operator bool() const __attribute__((always_inline)) { return fn; } operator FunVPPtr() const { return vPtrToFunPtrExec; } void* arg() const { return reinterpret_cast(fn); } /// Calling is safe without checking for nullptr. /// If non-void, returns the default value. /// In ISR context, where faults and exceptions must not /// occurs, this saves the extra check for nullptr, /// and allows the compiler to optimize out checks /// in std::function which may not be ISR-safe or /// cause linker errors, like l32r relocation errors /// on the Xtensa ISA. inline R IRAM_ATTR operator()(P... args) const __attribute__((always_inline)) { if (fn) return fn(std::forward(args...)); return R(); } protected: FunPtr fn; }; #endif #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) template class DelegateImpl { public: using target_type = R(); protected: using FunPtr = target_type*; using FunAPtr = R(*)(A); using FunctionType = std::function; using FunVPPtr = R(*)(void*); public: DelegateImpl() { kind = FP; fn = nullptr; } DelegateImpl(std::nullptr_t) { kind = FP; fn = nullptr; } ~DelegateImpl() { if (FUNC == kind) functional.~FunctionType(); else if (FPA == kind) obj.~A(); } DelegateImpl(const DelegateImpl& del) { kind = del.kind; if (FUNC == del.kind) { new (&functional) FunctionType(del.functional); } else if (FPA == del.kind) { fnA = del.fnA; new (&obj) A(del.obj); } else { fn = del.fn; } } DelegateImpl(DelegateImpl&& del) { kind = del.kind; if (FUNC == del.kind) { new (&functional) FunctionType(std::move(del.functional)); } else if (FPA == del.kind) { fnA = del.fnA; new (&obj) A(std::move(del.obj)); } else { fn = del.fn; } } DelegateImpl(FunAPtr fnA, const A& obj) { kind = FPA; DelegateImpl::fnA = fnA; new (&this->obj) A(obj); } DelegateImpl(FunAPtr fnA, A&& obj) { kind = FPA; DelegateImpl::fnA = fnA; new (&this->obj) A(std::move(obj)); } DelegateImpl(FunPtr fn) { kind = FP; DelegateImpl::fn = fn; } template DelegateImpl(F functional) { kind = FUNC; new (&this->functional) FunctionType(std::forward(functional)); } DelegateImpl& operator=(const DelegateImpl& del) { if (this == &del) return *this; if (kind != del.kind) { if (FUNC == kind) { functional.~FunctionType(); } else if (FPA == kind) { obj.~A(); } if (FUNC == del.kind) { new (&this->functional) FunctionType(); } else if (FPA == del.kind) { new (&obj) A; } kind = del.kind; } if (FUNC == del.kind) { functional = del.functional; } else if (FPA == del.kind) { fnA = del.fnA; obj = del.obj; } else { fn = del.fn; } return *this; } DelegateImpl& operator=(DelegateImpl&& del) { if (this == &del) return *this; if (kind != del.kind) { if (FUNC == kind) { functional.~FunctionType(); } else if (FPA == kind) { obj.~A(); } if (FUNC == del.kind) { new (&this->functional) FunctionType(); } else if (FPA == del.kind) { new (&obj) A; } kind = del.kind; } if (FUNC == del.kind) { functional = std::move(del.functional); } else if (FPA == del.kind) { fnA = del.fnA; obj = std::move(del.obj); } else { fn = del.fn; } return *this; } DelegateImpl& operator=(FunPtr fn) { if (FUNC == kind) { functional.~FunctionType(); } else if (FPA == kind) { obj.~A(); } kind = FP; this->fn = fn; return *this; } DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) { if (FUNC == kind) { functional.~FunctionType(); } else if (FPA == kind) { obj.~A(); } kind = FP; fn = nullptr; return *this; } IRAM_ATTR operator bool() const { if (FP == kind) { return fn; } else if (FPA == kind) { return fnA; } else { return functional ? true : false; } } static inline R IRAM_ATTR vPtrToFunAPtrExec(void* self) __attribute__((always_inline)) { return static_cast(self)->fnA( static_cast(self)->obj); }; operator FunVPPtr() const { if (FP == kind) { return reinterpret_cast(fn); } else if (FPA == kind) { return vPtrToFunAPtrExec; } else { return [](void* self) -> R { return static_cast(self)->functional(); }; } } void* arg() const { if (FP == kind) { return nullptr; } else { return const_cast(this); } } operator FunctionType() const { if (FP == kind) { return fn; } else if (FPA == kind) { return [this]() { return fnA(obj); }; } else { return functional; } } /// Calling is safe without checking for nullptr. /// If non-void, returns the default value. /// In ISR context, where faults and exceptions must not /// occurs, this saves the extra check for nullptr, /// and allows the compiler to optimize out checks /// in std::function which may not be ISR-safe or /// cause linker errors, like l32r relocation errors /// on the Xtensa ISA. R IRAM_ATTR operator()() const { if (FP == kind) { if (fn) return fn(); } else if (FPA == kind) { if (fnA) return fnA(obj); } else { if (functional) return functional(); } return R(); } protected: union { FunctionType functional; FunPtr fn; struct { FunAPtr fnA; A obj; }; }; enum { FUNC, FP, FPA } kind; }; #else template class DelegateImpl { public: using target_type = R(); protected: using FunPtr = target_type*; using FunAPtr = R(*)(A); using FunVPPtr = R(*)(void*); public: DelegateImpl() { kind = FP; fn = nullptr; } DelegateImpl(std::nullptr_t) { kind = FP; fn = nullptr; } DelegateImpl(const DelegateImpl& del) { kind = del.kind; if (FPA == del.kind) { fnA = del.fnA; obj = del.obj; } else { fn = del.fn; } } DelegateImpl(DelegateImpl&& del) { kind = del.kind; if (FPA == del.kind) { fnA = del.fnA; obj = std::move(del.obj); } else { fn = del.fn; } } DelegateImpl(FunAPtr fnA, const A& obj) { kind = FPA; DelegateImpl::fnA = fnA; this->obj = obj; } DelegateImpl(FunAPtr fnA, A&& obj) { kind = FPA; DelegateImpl::fnA = fnA; this->obj = std::move(obj); } DelegateImpl(FunPtr fn) { kind = FP; DelegateImpl::fn = fn; } template DelegateImpl(F fn) { kind = FP; DelegateImpl::fn = std::forward(fn); } DelegateImpl& operator=(const DelegateImpl& del) { if (this == &del) return *this; if (kind != del.kind) { if (FPA == kind) { obj = {}; } kind = del.kind; } if (FPA == del.kind) { fnA = del.fnA; obj = del.obj; } else { fn = del.fn; } return *this; } DelegateImpl& operator=(DelegateImpl&& del) { if (this == &del) return *this; if (kind != del.kind) { if (FPA == kind) { obj = {}; } kind = del.kind; } if (FPA == del.kind) { fnA = del.fnA; obj = std::move(del.obj); } else { fn = del.fn; } return *this; } DelegateImpl& operator=(FunPtr fn) { if (FPA == kind) { obj = {}; } kind = FP; this->fn = fn; return *this; } DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) { if (FPA == kind) { obj = {}; } kind = FP; fn = nullptr; return *this; } IRAM_ATTR operator bool() const { if (FP == kind) { return fn; } else { return fnA; } } static inline R IRAM_ATTR vPtrToFunAPtrExec(void* self) __attribute__((always_inline)) { return static_cast(self)->fnA( static_cast(self)->obj); }; operator FunVPPtr() const { if (FP == kind) { return reinterpret_cast(fn); } else { return vPtrToFunAPtrExec; } } void* arg() const { if (FP == kind) { return nullptr; } else { return const_cast(this); } } /// Calling is safe without checking for nullptr. /// If non-void, returns the default value. /// In ISR context, where faults and exceptions must not /// occurs, this saves the extra check for nullptr, /// and allows the compiler to optimize out checks /// in std::function which may not be ISR-safe or /// cause linker errors, like l32r relocation errors /// on the Xtensa ISA. R IRAM_ATTR operator()() const { if (FP == kind) { if (fn) return fn(); } else { if (fnA) return fnA(obj); } return R(); } protected: union { FunPtr fn; FunAPtr fnA; }; A obj; enum { FP, FPA } kind; }; #endif #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) template class DelegateImpl { public: using target_type = R(); protected: using FunPtr = target_type*; using FunctionType = std::function; using FunVPPtr = R(*)(void*); public: DelegateImpl() { kind = FP; fn = nullptr; } DelegateImpl(std::nullptr_t) { kind = FP; fn = nullptr; } ~DelegateImpl() { if (FUNC == kind) functional.~FunctionType(); } DelegateImpl(const DelegateImpl& del) { kind = del.kind; if (FUNC == del.kind) { new (&functional) FunctionType(del.functional); } else { fn = del.fn; } } DelegateImpl(DelegateImpl&& del) { kind = del.kind; if (FUNC == del.kind) { new (&functional) FunctionType(std::move(del.functional)); } else { fn = del.fn; } } DelegateImpl(FunPtr fn) { kind = FP; DelegateImpl::fn = fn; } template DelegateImpl(F functional) { kind = FUNC; new (&this->functional) FunctionType(std::forward(functional)); } DelegateImpl& operator=(const DelegateImpl& del) { if (this == &del) return *this; if (FUNC == kind && FUNC != del.kind) { functional.~FunctionType(); } else if (FUNC != kind && FUNC == del.kind) { new (&this->functional) FunctionType(); } kind = del.kind; if (FUNC == del.kind) { functional = del.functional; } else { fn = del.fn; } return *this; } DelegateImpl& operator=(DelegateImpl&& del) { if (this == &del) return *this; if (FUNC == kind && FUNC != del.kind) { functional.~FunctionType(); } else if (FUNC != kind && FUNC == del.kind) { new (&this->functional) FunctionType(); } kind = del.kind; if (FUNC == del.kind) { functional = std::move(del.functional); } else { fn = del.fn; } return *this; } DelegateImpl& operator=(FunPtr fn) { if (FUNC == kind) { functional.~FunctionType(); kind = FP; } DelegateImpl::fn = fn; return *this; } DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) { if (FUNC == kind) { functional.~FunctionType(); } kind = FP; fn = nullptr; return *this; } IRAM_ATTR operator bool() const { if (FP == kind) { return fn; } else { return functional ? true : false; } } operator FunVPPtr() const { if (FP == kind) { return reinterpret_cast(fn); } else { return [](void* self) -> R { return static_cast(self)->functional(); }; } } void* arg() const { if (FP == kind) { return nullptr; } else { return const_cast(this); } } operator FunctionType() const { if (FP == kind) { return fn; } else { return functional; } } /// Calling is safe without checking for nullptr. /// If non-void, returns the default value. /// In ISR context, where faults and exceptions must not /// occurs, this saves the extra check for nullptr, /// and allows the compiler to optimize out checks /// in std::function which may not be ISR-safe or /// cause linker errors, like l32r relocation errors /// on the Xtensa ISA. R IRAM_ATTR operator()() const { if (FP == kind) { if (fn) return fn(); } else { if (functional) return functional(); } return R(); } protected: union { FunctionType functional; FunPtr fn; }; enum { FUNC, FP } kind; }; #else template class DelegateImpl { public: using target_type = R(); protected: using FunPtr = target_type*; using FunVPPtr = R(*)(void*); public: DelegateImpl() { fn = nullptr; } DelegateImpl(std::nullptr_t) { fn = nullptr; } DelegateImpl(const DelegateImpl& del) { fn = del.fn; } DelegateImpl(DelegateImpl&& del) { fn = std::move(del.fn); } DelegateImpl(FunPtr fn) { DelegateImpl::fn = fn; } template DelegateImpl(F fn) { DelegateImpl::fn = std::forward(fn); } DelegateImpl& operator=(const DelegateImpl& del) { if (this == &del) return *this; fn = del.fn; return *this; } DelegateImpl& operator=(DelegateImpl&& del) { if (this == &del) return *this; fn = std::move(del.fn); return *this; } DelegateImpl& operator=(FunPtr fn) { DelegateImpl::fn = fn; return *this; } inline DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { fn = nullptr; return *this; } inline IRAM_ATTR operator bool() const __attribute__((always_inline)) { return fn; } operator FunVPPtr() const { return reinterpret_cast(fn); } void* arg() const { return nullptr; } /// Calling is safe without checking for nullptr. /// If non-void, returns the default value. /// In ISR context, where faults and exceptions must not /// occurs, this saves the extra check for nullptr, /// and allows the compiler to optimize out checks /// in std::function which may not be ISR-safe or /// cause linker errors, like l32r relocation errors /// on the Xtensa ISA. inline R IRAM_ATTR operator()() const __attribute__((always_inline)) { if (fn) return fn(); return R(); } protected: FunPtr fn; }; #endif template class Delegate : private detail::DelegatePImpl { public: using target_type = R(P...); protected: using FunPtr = target_type*; using FunAPtr = R(*)(A, P...); using FunVPPtr = R(*)(void*, P...); #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) using FunctionType = std::function; #endif public: using detail::DelegatePImpl::operator bool; using detail::DelegatePImpl::arg; using detail::DelegatePImpl::operator(); operator FunVPPtr() { return detail::DelegatePImpl::operator FunVPPtr(); } #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) operator FunctionType() { return detail::DelegatePImpl::operator FunctionType(); } #endif Delegate() : detail::DelegatePImpl::DelegatePImpl() {} Delegate(std::nullptr_t) : detail::DelegatePImpl::DelegatePImpl(nullptr) {} Delegate(const Delegate& del) : detail::DelegatePImpl::DelegatePImpl( static_cast&>(del)) {} Delegate(Delegate&& del) : detail::DelegatePImpl::DelegatePImpl( std::move(static_cast&>(del))) {} Delegate(FunAPtr fnA, const A& obj) : detail::DelegatePImpl::DelegatePImpl(fnA, obj) {} Delegate(FunAPtr fnA, A&& obj) : detail::DelegatePImpl::DelegatePImpl(fnA, std::move(obj)) {} Delegate(FunPtr fn) : detail::DelegatePImpl::DelegatePImpl(fn) {} template Delegate(F functional) : detail::DelegatePImpl::DelegatePImpl(std::forward(functional)) {} Delegate& operator=(const Delegate& del) { detail::DelegatePImpl::operator=(del); return *this; } Delegate& operator=(Delegate&& del) { detail::DelegatePImpl::operator=(std::move(del)); return *this; } Delegate& operator=(FunPtr fn) { detail::DelegatePImpl::operator=(fn); return *this; } inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { detail::DelegatePImpl::operator=(nullptr); return *this; } }; template class Delegate : private detail::DelegatePImpl { public: using target_type = R(P...); protected: using FunPtr = target_type*; using FunAPtr = R(*)(A*, P...); using FunVPPtr = R(*)(void*, P...); #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) using FunctionType = std::function; #endif public: using detail::DelegatePImpl::operator bool; using detail::DelegatePImpl::operator(); operator FunVPPtr() const { if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) { return reinterpret_cast(detail::DelegatePImpl::fnA); } else { return detail::DelegatePImpl::operator FunVPPtr(); } } #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) operator FunctionType() { return detail::DelegatePImpl::operator FunctionType(); } #endif void* arg() const { if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) { return detail::DelegatePImpl::obj; } else { return detail::DelegatePImpl::arg(); } } Delegate() : detail::DelegatePImpl::DelegatePImpl() {} Delegate(std::nullptr_t) : detail::DelegatePImpl::DelegatePImpl(nullptr) {} Delegate(const Delegate& del) : detail::DelegatePImpl::DelegatePImpl( static_cast&>(del)) {} Delegate(Delegate&& del) : detail::DelegatePImpl::DelegatePImpl( std::move(static_cast&>(del))) {} Delegate(FunAPtr fnA, A* obj) : detail::DelegatePImpl::DelegatePImpl(fnA, obj) {} Delegate(FunPtr fn) : detail::DelegatePImpl::DelegatePImpl(fn) {} template Delegate(F functional) : detail::DelegatePImpl::DelegatePImpl(std::forward(functional)) {} Delegate& operator=(const Delegate& del) { detail::DelegatePImpl::operator=(del); return *this; } Delegate& operator=(Delegate&& del) { detail::DelegatePImpl::operator=(std::move(del)); return *this; } Delegate& operator=(FunPtr fn) { detail::DelegatePImpl::operator=(fn); return *this; } inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { detail::DelegatePImpl::operator=(nullptr); return *this; } }; template class Delegate : private detail::DelegatePImpl { public: using target_type = R(P...); protected: using FunPtr = target_type*; #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) using FunctionType = std::function; #endif using FunVPPtr = R(*)(void*, P...); public: using detail::DelegatePImpl::operator bool; using detail::DelegatePImpl::arg; using detail::DelegatePImpl::operator(); operator FunVPPtr() const { return detail::DelegatePImpl::operator FunVPPtr(); } #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) operator FunctionType() { return detail::DelegatePImpl::operator FunctionType(); } #endif Delegate() : detail::DelegatePImpl::DelegatePImpl() {} Delegate(std::nullptr_t) : detail::DelegatePImpl::DelegatePImpl(nullptr) {} Delegate(const Delegate& del) : detail::DelegatePImpl::DelegatePImpl( static_cast&>(del)) {} Delegate(Delegate&& del) : detail::DelegatePImpl::DelegatePImpl( std::move(static_cast&>(del))) {} Delegate(FunPtr fn) : detail::DelegatePImpl::DelegatePImpl(fn) {} template Delegate(F functional) : detail::DelegatePImpl::DelegatePImpl(std::forward(functional)) {} Delegate& operator=(const Delegate& del) { detail::DelegatePImpl::operator=(del); return *this; } Delegate& operator=(Delegate&& del) { detail::DelegatePImpl::operator=(std::move(del)); return *this; } Delegate& operator=(FunPtr fn) { detail::DelegatePImpl::operator=(fn); return *this; } inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { detail::DelegatePImpl::operator=(nullptr); return *this; } }; template class Delegate : private detail::DelegateImpl { public: using target_type = R(); protected: using FunPtr = target_type*; using FunAPtr = R(*)(A); using FunVPPtr = R(*)(void*); #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) using FunctionType = std::function; #endif public: using detail::DelegateImpl::operator bool; using detail::DelegateImpl::arg; using detail::DelegateImpl::operator(); operator FunVPPtr() { return detail::DelegateImpl::operator FunVPPtr(); } #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) operator FunctionType() { return detail::DelegateImpl::operator FunctionType(); } #endif Delegate() : detail::DelegateImpl::DelegateImpl() {} Delegate(std::nullptr_t) : detail::DelegateImpl::DelegateImpl(nullptr) {} Delegate(const Delegate& del) : detail::DelegateImpl::DelegateImpl( static_cast&>(del)) {} Delegate(Delegate&& del) : detail::DelegateImpl::DelegateImpl( std::move(static_cast&>(del))) {} Delegate(FunAPtr fnA, const A& obj) : detail::DelegateImpl::DelegateImpl(fnA, obj) {} Delegate(FunAPtr fnA, A&& obj) : detail::DelegateImpl::DelegateImpl(fnA, std::move(obj)) {} Delegate(FunPtr fn) : detail::DelegateImpl::DelegateImpl(fn) {} template Delegate(F functional) : detail::DelegateImpl::DelegateImpl(std::forward(functional)) {} Delegate& operator=(const Delegate& del) { detail::DelegateImpl::operator=(del); return *this; } Delegate& operator=(Delegate&& del) { detail::DelegateImpl::operator=(std::move(del)); return *this; } Delegate& operator=(FunPtr fn) { detail::DelegateImpl::operator=(fn); return *this; } inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { detail::DelegateImpl::operator=(nullptr); return *this; } }; template class Delegate : private detail::DelegateImpl { public: using target_type = R(); protected: using FunPtr = target_type*; using FunAPtr = R(*)(A*); using FunVPPtr = R(*)(void*); #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) using FunctionType = std::function; #endif public: using detail::DelegateImpl::operator bool; using detail::DelegateImpl::operator(); operator FunVPPtr() const { if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) { return reinterpret_cast(detail::DelegateImpl::fnA); } else { return detail::DelegateImpl::operator FunVPPtr(); } } #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) operator FunctionType() { return detail::DelegateImpl::operator FunctionType(); } #endif void* arg() const { if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) { return detail::DelegateImpl::obj; } else { return detail::DelegateImpl::arg(); } } Delegate() : detail::DelegateImpl::DelegateImpl() {} Delegate(std::nullptr_t) : detail::DelegateImpl::DelegateImpl(nullptr) {} Delegate(const Delegate& del) : detail::DelegateImpl::DelegateImpl( static_cast&>(del)) {} Delegate(Delegate&& del) : detail::DelegateImpl::DelegateImpl( std::move(static_cast&>(del))) {} Delegate(FunAPtr fnA, A* obj) : detail::DelegateImpl::DelegateImpl(fnA, obj) {} Delegate(FunPtr fn) : detail::DelegateImpl::DelegateImpl(fn) {} template Delegate(F functional) : detail::DelegateImpl::DelegateImpl(std::forward(functional)) {} Delegate& operator=(const Delegate& del) { detail::DelegateImpl::operator=(del); return *this; } Delegate& operator=(Delegate&& del) { detail::DelegateImpl::operator=(std::move(del)); return *this; } Delegate& operator=(FunPtr fn) { detail::DelegateImpl::operator=(fn); return *this; } inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { detail::DelegateImpl::operator=(nullptr); return *this; } }; template class Delegate : private detail::DelegateImpl { public: using target_type = R(); protected: using FunPtr = target_type*; #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) using FunctionType = std::function; #endif using FunVPPtr = R(*)(void*); public: using detail::DelegateImpl::operator bool; using detail::DelegateImpl::arg; using detail::DelegateImpl::operator(); operator FunVPPtr() const { return detail::DelegateImpl::operator FunVPPtr(); } #if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) operator FunctionType() { return detail::DelegateImpl::operator FunctionType(); } #endif Delegate() : detail::DelegateImpl::DelegateImpl() {} Delegate(std::nullptr_t) : detail::DelegateImpl::DelegateImpl(nullptr) {} Delegate(const Delegate& del) : detail::DelegateImpl::DelegateImpl( static_cast&>(del)) {} Delegate(Delegate&& del) : detail::DelegateImpl::DelegateImpl( std::move(static_cast&>(del))) {} Delegate(FunPtr fn) : detail::DelegateImpl::DelegateImpl(fn) {} template Delegate(F functional) : detail::DelegateImpl::DelegateImpl(std::forward(functional)) {} Delegate& operator=(const Delegate& del) { detail::DelegateImpl::operator=(del); return *this; } Delegate& operator=(Delegate&& del) { detail::DelegateImpl::operator=(std::move(del)); return *this; } Delegate& operator=(FunPtr fn) { detail::DelegateImpl::operator=(fn); return *this; } inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { detail::DelegateImpl::operator=(nullptr); return *this; } }; } } template class Delegate; template class Delegate : public delegate::detail::Delegate { public: Delegate() : delegate::detail::Delegate::Delegate() {} Delegate(std::nullptr_t) : delegate::detail::Delegate::Delegate(nullptr) {} Delegate(const Delegate& del) : delegate::detail::Delegate::Delegate( static_cast&>(del)) {} Delegate(Delegate&& del) : delegate::detail::Delegate::Delegate( std::move(static_cast&>(del))) {} Delegate(typename delegate::detail::Delegate::FunAPtr fnA, const A& obj) : delegate::detail::Delegate::Delegate(fnA, obj) {} Delegate(typename delegate::detail::Delegate::FunAPtr fnA, A&& obj) : delegate::detail::Delegate::Delegate(fnA, std::move(obj)) {} Delegate(typename delegate::detail::Delegate::FunPtr fn) : delegate::detail::Delegate::Delegate(fn) {} template Delegate(F functional) : delegate::detail::Delegate::Delegate(std::forward(functional)) {} Delegate& operator=(const Delegate& del) { delegate::detail::Delegate::operator=(del); return *this; } Delegate& operator=(Delegate&& del) { delegate::detail::Delegate::operator=(std::move(del)); return *this; } Delegate& operator=(typename delegate::detail::Delegate::FunPtr fn) { delegate::detail::Delegate::operator=(fn); return *this; } inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { delegate::detail::Delegate::operator=(nullptr); return *this; } }; template class Delegate : public delegate::detail::Delegate { public: Delegate() : delegate::detail::Delegate::Delegate() {} Delegate(std::nullptr_t) : delegate::detail::Delegate::Delegate(nullptr) {} Delegate(const Delegate& del) : delegate::detail::Delegate::Delegate( static_cast&>(del)) {} Delegate(Delegate&& del) : delegate::detail::Delegate::Delegate( std::move(static_cast&>(del))) {} Delegate(typename delegate::detail::Delegate::FunPtr fn) : delegate::detail::Delegate::Delegate(fn) {} template Delegate(F functional) : delegate::detail::Delegate::Delegate(std::forward(functional)) {} Delegate& operator=(const Delegate& del) { delegate::detail::Delegate::operator=(del); return *this; } Delegate& operator=(Delegate&& del) { delegate::detail::Delegate::operator=(std::move(del)); return *this; } Delegate& operator=(typename delegate::detail::Delegate::FunPtr fn) { delegate::detail::Delegate::operator=(fn); return *this; } inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { delegate::detail::Delegate::operator=(nullptr); return *this; } }; #endif // __Delegate_h