diff --git a/cores/esp8266/Delegate.h b/cores/esp8266/Delegate.h new file mode 100644 index 0000000000..193ca8a8fe --- /dev/null +++ b/cores/esp8266/Delegate.h @@ -0,0 +1,2130 @@ +/* +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 + 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; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return fnA; + } + else + { + return functional ? true : false; + } + } + + static R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) + { + 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; + } + } + + R IRAM_ATTR operator()(P... args) const + { + if (FP == kind) + { + return fn(std::forward(args...)); + } + else if (FPA == kind) + { + return fnA(obj, std::forward(args...)); + } + else + { + return functional(std::forward(args...)); + } + } + + 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; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return fnA; + } + } + + static R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) + { + 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); + } + } + + R IRAM_ATTR operator()(P... args) const + { + if (FP == kind) + { + return fn(std::forward(args...)); + } + else + { + return fnA(obj, std::forward(args...)); + } + } + + 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; + } + + 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; + } + } + + R IRAM_ATTR operator()(P... args) const + { + if (FP == kind) + { + return fn(std::forward(args...)); + } + else + { + return functional(std::forward(args...)); + } + } + + 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; + } + + DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) + { + fn = nullptr; + return *this; + } + + operator bool() const + { + return fn; + } + + operator FunVPPtr() const + { + return vPtrToFunPtrExec; + } + + void* arg() const + { + return reinterpret_cast(fn); + } + + R IRAM_ATTR operator()(P... args) const + { + return fn(std::forward(args...)); + } + + 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; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return fnA; + } + else + { + return functional ? true : false; + } + } + + static R IRAM_ATTR vPtrToFunAPtrExec(void* self) + { + 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; + } + } + + R IRAM_ATTR operator()() const + { + if (FP == kind) + { + return fn(); + } + else if (FPA == kind) + { + return fnA(obj); + } + else + { + return functional(); + } + } + + 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; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return fnA; + } + } + + static R IRAM_ATTR vPtrToFunAPtrExec(void* self) + { + 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); + } + } + + R IRAM_ATTR operator()() const + { + if (FP == kind) + { + return fn(); + } + else + { + return fnA(obj); + } + } + + 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; + } + + 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; + } + } + + R IRAM_ATTR operator()() const + { + if (FP == kind) + { + return fn(); + } + else + { + return functional(); + } + } + + 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; + } + + DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) + { + fn = nullptr; + return *this; + } + + operator bool() const + { + return fn; + } + + operator FunVPPtr() const + { + return reinterpret_cast(fn); + } + + void* arg() const + { + return nullptr; + } + + R IRAM_ATTR operator()() const + { + return fn(); + } + + 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; + } + + Delegate& IRAM_ATTR operator=(std::nullptr_t) { + 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; + } + + Delegate& IRAM_ATTR operator=(std::nullptr_t) { + 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; + } + + Delegate& IRAM_ATTR operator=(std::nullptr_t) { + 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; + } + + Delegate& IRAM_ATTR operator=(std::nullptr_t) { + 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; + } + + Delegate& IRAM_ATTR operator=(std::nullptr_t) { + 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; + } + + Delegate& IRAM_ATTR operator=(std::nullptr_t) { + 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; + } + + Delegate& IRAM_ATTR operator=(std::nullptr_t) { + 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; + } + + Delegate& IRAM_ATTR operator=(std::nullptr_t) { + delegate::detail::Delegate::operator=(nullptr); + return *this; + } +}; + +#endif // __Delegate_h diff --git a/cores/esp8266/MultiDelegate.h b/cores/esp8266/MultiDelegate.h new file mode 100644 index 0000000000..36cbd94b6d --- /dev/null +++ b/cores/esp8266/MultiDelegate.h @@ -0,0 +1,567 @@ +/* +MultiDelegate.h - A queue or event multiplexer based on the efficient Delegate +class +Copyright (c) 2019-2020 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 __MULTIDELEGATE_H +#define __MULTIDELEGATE_H + +#include +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +#include +#else +#include "circular_queue/ghostl.h" +#endif + +#if defined(ESP8266) +#include +using esp8266::InterruptLock; +#elif defined(ARDUINO) +class InterruptLock { +public: + InterruptLock() { + noInterrupts(); + } + ~InterruptLock() { + interrupts(); + } +}; +#else +#include +#endif + +namespace +{ + + template< typename Delegate, typename R, bool ISQUEUE = false, typename... P> + struct CallP + { + static R execute(Delegate& del, P... args) + { + return del(std::forward(args...)); + } + }; + + template< typename Delegate, bool ISQUEUE, typename... P> + struct CallP + { + static bool execute(Delegate& del, P... args) + { + del(std::forward(args...)); + return true; + } + }; + + template< typename Delegate, typename R, bool ISQUEUE = false> + struct Call + { + static R execute(Delegate& del) + { + return del(); + } + }; + + template< typename Delegate, bool ISQUEUE> + struct Call + { + static bool execute(Delegate& del) + { + del(); + return true; + } + }; + +} + +namespace delegate +{ + namespace detail + { + + template< typename Delegate, typename R, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32, typename... P> + class MultiDelegatePImpl + { + public: + MultiDelegatePImpl() = default; + ~MultiDelegatePImpl() + { + *this = nullptr; + } + + MultiDelegatePImpl(const MultiDelegatePImpl&) = delete; + MultiDelegatePImpl& operator=(const MultiDelegatePImpl&) = delete; + + MultiDelegatePImpl(MultiDelegatePImpl&& md) + { + first = md.first; + last = md.last; + unused = md.unused; + nodeCount = md.nodeCount; + md.first = nullptr; + md.last = nullptr; + md.unused = nullptr; + md.nodeCount = 0; + } + + MultiDelegatePImpl(const Delegate& del) + { + add(del); + } + + MultiDelegatePImpl(Delegate&& del) + { + add(std::move(del)); + } + + MultiDelegatePImpl& operator=(MultiDelegatePImpl&& md) + { + first = md.first; + last = md.last; + unused = md.unused; + nodeCount = md.nodeCount; + md.first = nullptr; + md.last = nullptr; + md.unused = nullptr; + md.nodeCount = 0; + return *this; + } + + MultiDelegatePImpl& operator=(std::nullptr_t) + { + if (last) + last->mNext = unused; + if (first) + unused = first; + while (unused) + { + auto to_delete = unused; + unused = unused->mNext; + delete(to_delete); + } + return *this; + } + + MultiDelegatePImpl& operator+=(const Delegate& del) + { + add(del); + return *this; + } + + MultiDelegatePImpl& operator+=(Delegate&& del) + { + add(std::move(del)); + return *this; + } + + protected: + struct Node_t + { + ~Node_t() + { + mDelegate = nullptr; // special overload in Delegate + } + Node_t* mNext = nullptr; + Delegate mDelegate; + }; + + Node_t* first = nullptr; + Node_t* last = nullptr; + Node_t* unused = nullptr; + size_t nodeCount = 0; + + // Returns a pointer to an unused Node_t, + // or if none are available allocates a new one, + // or nullptr if limit is reached + Node_t* IRAM_ATTR get_node_unsafe() + { + Node_t* result = nullptr; + // try to get an item from unused items list + if (unused) + { + result = unused; + unused = unused->mNext; + } + // if no unused items, and count not too high, allocate a new one + else if (nodeCount < QUEUE_CAPACITY) + { +#if defined(ESP8266) || defined(ESP32) + result = new (std::nothrow) Node_t; +#else + result = new Node_t; +#endif + if (result) + ++nodeCount; + } + return result; + } + + void recycle_node_unsafe(Node_t* node) + { + node->mDelegate = nullptr; // special overload in Delegate + node->mNext = unused; + unused = node; + } + +#ifndef ARDUINO + std::mutex mutex_unused; +#endif + public: + class iterator : public std::iterator + { + public: + Node_t* current = nullptr; + Node_t* prev = nullptr; + const Node_t* stop = nullptr; + + iterator(MultiDelegatePImpl& md) : current(md.first), stop(md.last) {} + iterator() = default; + iterator(const iterator&) = default; + iterator& operator=(const iterator&) = default; + iterator& operator=(iterator&&) = default; + operator bool() const + { + return current && stop; + } + bool operator==(const iterator& rhs) const + { + return current == rhs.current; + } + bool operator!=(const iterator& rhs) const + { + return !operator==(rhs); + } + Delegate& operator*() const + { + return current->mDelegate; + } + Delegate* operator->() const + { + return ¤t->mDelegate; + } + iterator& operator++() // prefix + { + if (current && stop != current) + { + prev = current; + current = current->mNext; + } + else + current = nullptr; // end + return *this; + } + iterator& operator++(int) // postfix + { + iterator tmp(*this); + operator++(); + return tmp; + } + }; + + iterator begin() + { + return iterator(*this); + } + iterator end() const + { + return iterator(); + } + + const Delegate* IRAM_ATTR add(const Delegate& del) + { + return add(Delegate(del)); + } + + const Delegate* IRAM_ATTR add(Delegate&& del) + { + if (!del) + return nullptr; + +#ifdef ARDUINO + InterruptLock lockAllInterruptsInThisScope; +#else + std::lock_guard lock(mutex_unused); +#endif + + Node_t* item = ISQUEUE ? get_node_unsafe() : +#if defined(ESP8266) || defined(ESP32) + new (std::nothrow) Node_t; +#else + new Node_t; +#endif + if (!item) + return nullptr; + + item->mDelegate = std::move(del); + item->mNext = nullptr; + + if (last) + last->mNext = item; + else + first = item; + last = item; + + return &item->mDelegate; + } + + iterator erase(iterator it) + { + if (!it) + return end(); +#ifdef ARDUINO + InterruptLock lockAllInterruptsInThisScope; +#else + std::lock_guard lock(mutex_unused); +#endif + auto to_recycle = it.current; + + if (last == it.current) + last = it.prev; + it.current = it.current->mNext; + if (it.prev) + { + it.prev->mNext = it.current; + } + else + { + first = it.current; + } + if (ISQUEUE) + recycle_node_unsafe(to_recycle); + else + delete to_recycle; + return it; + } + + bool erase(const Delegate* const del) + { + auto it = begin(); + while (it) + { + if (del == &(*it)) + { + erase(it); + return true; + } + ++it; + } + return false; + } + + operator bool() const + { + return first; + } + + R operator()(P... args) + { + auto it = begin(); + if (!it) + return {}; + + static std::atomic fence(false); + // prevent recursive calls +#if defined(ARDUINO) && !defined(ESP32) + if (fence.load()) return {}; + fence.store(true); +#else + if (fence.exchange(true)) return {}; +#endif + + R result; + do + { + result = CallP::execute(*it, args...); + if (result && ISQUEUE) + it = erase(it); + else + ++it; +#if defined(ESP8266) || defined(ESP32) + // running callbacks might last too long for watchdog etc. + optimistic_yield(10000); +#endif + } while (it); + + fence.store(false); + return result; + } + }; + + template< typename Delegate, typename R = void, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32> + class MultiDelegateImpl : public MultiDelegatePImpl + { + public: + using MultiDelegatePImpl::MultiDelegatePImpl; + + R operator()() + { + auto it = this->begin(); + if (!it) + return {}; + + static std::atomic fence(false); + // prevent recursive calls +#if defined(ARDUINO) && !defined(ESP32) + if (fence.load()) return {}; + fence.store(true); +#else + if (fence.exchange(true)) return {}; +#endif + + R result; + do + { + result = Call::execute(*it); + if (result && ISQUEUE) + it = this->erase(it); + else + ++it; +#if defined(ESP8266) || defined(ESP32) + // running callbacks might last too long for watchdog etc. + optimistic_yield(10000); +#endif + } while (it); + + fence.store(false); + return result; + } + }; + + template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P> class MultiDelegate; + + template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P> + class MultiDelegate : public MultiDelegatePImpl + { + public: + using MultiDelegatePImpl::MultiDelegatePImpl; + }; + + template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY> + class MultiDelegate : public MultiDelegateImpl + { + public: + using MultiDelegateImpl::MultiDelegateImpl; + }; + + template< typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P> + class MultiDelegate : public MultiDelegatePImpl + { + public: + using MultiDelegatePImpl::MultiDelegatePImpl; + + void operator()(P... args) + { + auto it = this->begin(); + if (!it) + return; + + static std::atomic fence(false); + // prevent recursive calls +#if defined(ARDUINO) && !defined(ESP32) + if (fence.load()) return; + fence.store(true); +#else + if (fence.exchange(true)) return; +#endif + + do + { + CallP::execute(*it, args...); + if (ISQUEUE) + it = this->erase(it); + else + ++it; +#if defined(ESP8266) || defined(ESP32) + // running callbacks might last too long for watchdog etc. + optimistic_yield(10000); +#endif + } while (it); + + fence.store(false); + } + }; + + template< typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY> + class MultiDelegate : public MultiDelegateImpl + { + public: + using MultiDelegateImpl::MultiDelegateImpl; + + void operator()() + { + auto it = this->begin(); + if (!it) + return; + + static std::atomic fence(false); + // prevent recursive calls +#if defined(ARDUINO) && !defined(ESP32) + if (fence.load()) return; + fence.store(true); +#else + if (fence.exchange(true)) return; +#endif + + do + { + Call::execute(*it); + if (ISQUEUE) + it = this->erase(it); + else + ++it; +#if defined(ESP8266) || defined(ESP32) + // running callbacks might last too long for watchdog etc. + optimistic_yield(10000); +#endif + } while (it); + + fence.store(false); + } + }; + + } + +} + +/** +The MultiDelegate class template can be specialized to either a queue or an event multiplexer. +It is designed to be used with Delegate, the efficient runtime wrapper for C function ptr and C++ std::function. +@tparam Delegate specifies the concrete type that MultiDelegate bases the queue or event multiplexer on. +@tparam ISQUEUE modifies the generated MultiDelegate class in subtle ways. In queue mode (ISQUEUE == true), + the value of QUEUE_CAPACITY enforces the maximum number of simultaneous items the queue can contain. + This is exploited to minimize the use of new and delete by reusing already allocated items, thus + reducing heap fragmentation. In event multiplexer mode (ISQUEUE = false), new and delete are + used for allocation of the event handler items. + If the result type of the function call operator of Delegate is void, calling a MultiDelegate queue + removes each item after calling it; a Multidelegate event multiplexer keeps event handlers until + explicitly removed. + If the result type of the function call operator of Delegate is non-void, in a MultiDelegate queue + the type-conversion to bool of that result determines if the item is immediately removed or kept + after each call: if true is returned, the item is removed. A Multidelegate event multiplexer keeps event + handlers until they are explicitly removed. +@tparam QUEUE_CAPACITY is only used if ISQUEUE == true. Then, it sets the maximum capacity that the queue dynamically + allocates from the heap. Unused items are not returned to the heap, but are managed by the MultiDelegate + instance during its own lifetime for efficiency. +*/ +template< typename Delegate, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32> +class MultiDelegate : public delegate::detail::MultiDelegate +{ +public: + using delegate::detail::MultiDelegate::MultiDelegate; +}; + +#endif // __MULTIDELEGATE_H diff --git a/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino b/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino index 33c9435982..83b26e54f1 100644 --- a/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino +++ b/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino @@ -12,7 +12,9 @@ class ExampleClass { public: ExampleClass(int pin, int duration) : _pin(pin), _duration(duration) { pinMode(_pin, OUTPUT); - _myTicker.attach_ms(_duration, std::bind(&ExampleClass::classBlink, this)); + _myTicker.attach_ms(_duration, [this]() { + classBlink(); + }); } ~ExampleClass() {}; @@ -52,7 +54,7 @@ void setup() { scheduledTicker.attach_ms_scheduled(100, scheduledBlink); pinMode(LED4, OUTPUT); - parameterTicker.attach_ms(100, std::bind(parameterBlink, LED4)); + parameterTicker.attach_ms(100, parameterBlink, LED4); pinMode(LED5, OUTPUT); lambdaTicker.attach_ms(100, []() { diff --git a/libraries/Ticker/src/Ticker.cpp b/libraries/Ticker/src/Ticker.cpp index dca4435dc2..4aa23be650 100644 --- a/libraries/Ticker/src/Ticker.cpp +++ b/libraries/Ticker/src/Ticker.cpp @@ -33,7 +33,7 @@ Ticker::~Ticker() detach(); } -void Ticker::_attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t callback, void* arg) +void Ticker::_attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t* callback, void* arg) { if (_timer) { @@ -43,7 +43,6 @@ void Ticker::_attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t { _timer = &_etsTimer; } - os_timer_setfn(_timer, callback, arg); os_timer_arm(_timer, milliseconds, repeat); } @@ -55,17 +54,10 @@ void Ticker::detach() os_timer_disarm(_timer); _timer = nullptr; - _callback_function = nullptr; + _callback = nullptr; } bool Ticker::active() const { return _timer; } - -void Ticker::_static_callback(void* arg) -{ - Ticker* _this = reinterpret_cast(arg); - if (_this && _this->_callback_function) - _this->_callback_function(); -} diff --git a/libraries/Ticker/src/Ticker.h b/libraries/Ticker/src/Ticker.h index 791ff94567..39cf438825 100644 --- a/libraries/Ticker/src/Ticker.h +++ b/libraries/Ticker/src/Ticker.h @@ -22,7 +22,7 @@ #ifndef TICKER_H #define TICKER_H -#include +#include #include #include @@ -32,42 +32,42 @@ class Ticker Ticker(); ~Ticker(); - typedef void (*callback_with_arg_t)(void*); - typedef std::function callback_function_t; + using callback_t = Delegate; + using callback_with_arg_t = void(void*); // callback will be called at following loop() after ticker fires - void attach_scheduled(float seconds, callback_function_t callback) + void attach_scheduled(float seconds, callback_t callback) { - _callback_function = [callback]() { schedule_function(callback); }; - _attach_ms(1000UL * seconds, true); + _callback = [callback]() { schedule_function(callback); }; + _attach_ms(1000UL * seconds, true, _callback, _callback.arg()); } // callback will be called in SYS ctx when ticker fires - void attach(float seconds, callback_function_t callback) + void attach(float seconds, callback_t callback) { - _callback_function = std::move(callback); - _attach_ms(1000UL * seconds, true); + _callback = std::move(callback); + _attach_ms(1000UL * seconds, true, _callback, _callback.arg()); } // callback will be called at following loop() after ticker fires - void attach_ms_scheduled(uint32_t milliseconds, callback_function_t callback) + void attach_ms_scheduled(uint32_t milliseconds, callback_t callback) { - _callback_function = [callback]() { schedule_function(callback); }; - _attach_ms(milliseconds, true); + _callback = [callback]() { schedule_function(callback); }; + _attach_ms(milliseconds, true, _callback, _callback.arg()); } // callback will be called at following yield() after ticker fires - void attach_ms_scheduled_accurate(uint32_t milliseconds, callback_function_t callback) + void attach_ms_scheduled_accurate(uint32_t milliseconds, callback_t callback) { - _callback_function = [callback]() { schedule_recurrent_function_us([callback]() { callback(); return false; }, 0); }; - _attach_ms(milliseconds, true); + _callback = [callback]() { schedule_recurrent_function_us([callback]() { callback(); return false; }, 0); }; + _attach_ms(milliseconds, true, _callback, _callback.arg()); } // callback will be called in SYS ctx when ticker fires - void attach_ms(uint32_t milliseconds, callback_function_t callback) + void attach_ms(uint32_t milliseconds, callback_t callback) { - _callback_function = std::move(callback); - _attach_ms(milliseconds, true); + _callback = std::move(callback); + _attach_ms(milliseconds, true, _callback, _callback.arg()); } // callback will be called in SYS ctx when ticker fires @@ -77,7 +77,7 @@ class Ticker #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" static_assert(sizeof(TArg) <= sizeof(void*), "attach() callback argument size must be <= sizeof(void*)"); - _attach_ms(1000UL * seconds, true, reinterpret_cast(callback), reinterpret_cast(arg)); + _attach_ms(1000UL * seconds, true, reinterpret_cast(callback), reinterpret_cast(arg)); #pragma GCC diagnostic pop } @@ -88,36 +88,36 @@ class Ticker #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" static_assert(sizeof(TArg) <= sizeof(void*), "attach() callback argument size must be <= sizeof(void*)"); - _attach_ms(milliseconds, true, reinterpret_cast(callback), reinterpret_cast(arg)); + _attach_ms(milliseconds, true, reinterpret_cast(callback), reinterpret_cast(arg)); #pragma GCC diagnostic pop } // callback will be called at following loop() after ticker fires - void once_scheduled(float seconds, callback_function_t callback) + void once_scheduled(float seconds, callback_t callback) { - _callback_function = [callback]() { schedule_function(callback); }; - _attach_ms(1000UL * seconds, false); + _callback = [callback]() { schedule_function(callback); }; + _attach_ms(1000UL * seconds, false, _callback, _callback.arg()); } // callback will be called in SYS ctx when ticker fires - void once(float seconds, callback_function_t callback) + void once(float seconds, callback_t callback) { - _callback_function = std::move(callback); - _attach_ms(1000UL * seconds, false); + _callback = std::move(callback); + _attach_ms(1000UL * seconds, false, _callback, _callback.arg()); } // callback will be called at following loop() after ticker fires - void once_ms_scheduled(uint32_t milliseconds, callback_function_t callback) + void once_ms_scheduled(uint32_t milliseconds, callback_t callback) { - _callback_function = [callback]() { schedule_function(callback); }; - _attach_ms(milliseconds, false); + _callback = [callback]() { schedule_function(callback); }; + _attach_ms(milliseconds, false, _callback, _callback.arg()); } // callback will be called in SYS ctx when ticker fires - void once_ms(uint32_t milliseconds, callback_function_t callback) + void once_ms(uint32_t milliseconds, callback_t callback) { - _callback_function = std::move(callback); - _attach_ms(milliseconds, false); + _callback = std::move(callback); + _attach_ms(milliseconds, false, _callback, _callback.arg()); } // callback will be called in SYS ctx when ticker fires @@ -125,7 +125,7 @@ class Ticker void once(float seconds, void (*callback)(TArg), TArg arg) { static_assert(sizeof(TArg) <= sizeof(void*), "attach() callback argument size must be <= sizeof(void*)"); - _attach_ms(1000UL * seconds, false, reinterpret_cast(callback), reinterpret_cast(arg)); + _attach_ms(1000UL * seconds, false, reinterpret_cast(callback), reinterpret_cast(arg)); } // callback will be called in SYS ctx when ticker fires @@ -133,22 +133,17 @@ class Ticker void once_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg) { static_assert(sizeof(TArg) <= sizeof(void*), "attach() callback argument size must be <= sizeof(void*)"); - _attach_ms(milliseconds, false, reinterpret_cast(callback), reinterpret_cast(arg)); + _attach_ms(milliseconds, false, reinterpret_cast(callback), reinterpret_cast(arg)); } void detach(); bool active() const; protected: - static void _static_callback(void* arg); - void _attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t callback, void* arg); - void _attach_ms(uint32_t milliseconds, bool repeat) - { - _attach_ms(milliseconds, repeat, _static_callback, this); - } + void _attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t* callback, void* arg); ETSTimer* _timer; - callback_function_t _callback_function = nullptr; + callback_t _callback = nullptr; private: ETSTimer _etsTimer; diff --git a/libraries/esp8266/examples/DelegatePerf/DelegatePerf.ino b/libraries/esp8266/examples/DelegatePerf/DelegatePerf.ino new file mode 100644 index 0000000000..1d4912efc5 --- /dev/null +++ b/libraries/esp8266/examples/DelegatePerf/DelegatePerf.ino @@ -0,0 +1,192 @@ +/* + ESP8266 Delegate class template test by Dirk O. Kaar + This example code is in the public domain +*/ + +#include "Foo.h" +#ifndef NODELEGATE +#include +#include +#else +#include +#endif + +constexpr long unsigned MAXCNT = 100000UL; +const String TESTCASE = "F"; +const String LATENCY = "Latency/cycles = "; +uint32_t cycles; +uint32_t cnt; + +enum TestCase { F0 = 0, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12 }; +TestCase testCases[] = { F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12 }; + +using Fp0 = bool (*)(Foo*, int); +using Fp2 = bool(*)(int); + +Foo* o0; +Fp0 f0; +Foo* o1; +Fp0 f1; +#ifndef NODELEGATE +Delegate f2; +Delegate f3; +#else +std::function f2; +std::function f3; +#endif + +std::function f4; +std::function f5; + +#ifndef NODELEGATE +Delegate f6; +Delegate f7; + +Delegate f8; + +Delegate f9; +Delegate f10; + +Delegate f11; +Delegate f12; +#else +std::function f6; +std::function f7; + +std::function f8; + +std::function f9; +std::function f10; + +std::function f11; +std::function f12; +#endif + +void set_f0(Fp0 _f, Foo* _o) { + f0 = _f; + o0 = _o; +} +void set_f1(Fp0 _f, Foo* _o) { + f1 = _f; + o1 = _o; +} +void set_f2(Fp2 _f) { + f2 = { _f }; +} +void set_f3(Fp2 _f) { + f3 = { _f }; +} + +void set_f4(const std::function& _f) { + f4 = _f; +} +void set_f5(const std::function& _f) { + f5 = _f; +} + +#ifndef NODELEGATE +void set_f6(const Delegate& _f) { + f6 = _f; +} +void set_f7(const Delegate& _f) { + f7 = _f; +} + +void set_f8(const Delegate& _f) { + f8 = _f; +} + +void set_f9(const Delegate& _f) { + f9 = _f; +} +void set_f10(const Delegate& _f) { + f10 = _f; +} + +void set_f11(const Delegate& _f) { + f11 = _f; +} +void set_f12(const Delegate& _f) { + f12 = _f; +} +#else +void set_f6(const std::function& _f) { + f6 = _f; +} +void set_f7(const std::function& _f) { + f7 = _f; +} + +void set_f8(const std::function& _f) { + f8 = _f; +} + +void set_f9(const std::function& _f) { + f9 = _f; +} +void set_f10(const std::function& _f) { + f10 = _f; +} + +void set_f11(const std::function& _f) { + f11 = _f; +} +void set_f12(const std::function& _f) { + f12 = _f; +} +#endif + +extern void testPrep(); + +void stopWatch() { + if (MAXCNT == cnt) { + Serial.print(LATENCY); + Serial.println(cycles / MAXCNT); + cycles = 0; + cnt = 0; + } +} + +void setup() { + Serial.begin(115200); + testPrep(); + + cycles = 0; + cnt = 0; +} + +// Add the main program code into the continuous loop() function +void loop() { + for (auto tc : testCases) { + Serial.print(TESTCASE); + Serial.print(tc); + Serial.print(": "); + for (unsigned i = 0; i < MAXCNT; ++i) { + auto start = ESP.getCycleCount(); + switch (tc) { + case F0: f0(o0, 42); break; + case F1: f1(o1, 42); break; + case F2: f2(42); break; // { cbCPtr } + case F3: f3(42); break; // { cbCPtr } + + case F4: f4(42); break; // [o](int result) -> bool { return o->cb(result); } + case F5: f5(42); break; // std::bind(Foo::cbwObj, o, std::placeholders::_1) + + case F6: f6(42); break; // [o](int result) -> bool { return o->cb(result); } <==== antipattern for Delegate, use f11 instead + case F7: f7(42); break; // std::bind(Foo::cbwObj, o, std::placeholders::_1) <==== antipattern for Delegate, use f11 instead + + case F8: f8(42); break; // [](int result) -> bool { return cbCPtr(result); } + + case F9: f9(42); break; // [o](int result) -> bool { return o->cb(result); } <==== antipattern for Delegate, use f11 instead + case F10: f10(42); break; // std::bind(Foo::cbwObj, o, std::placeholders::_1) <==== antipattern for Delegate, use f11 instead + + case F11: f11(42); break; // [](Foo* o, int result) -> bool { return o->cb(result); }, o.get() }) + case F12: f12(42); break; // { Foo::cbwObj, o.get() } + } + cycles += (ESP.getCycleCount() - start); + stopWatch(); + } + yield(); + } + delay(16000); +} diff --git a/libraries/esp8266/examples/DelegatePerf/Foo.h b/libraries/esp8266/examples/DelegatePerf/Foo.h new file mode 100644 index 0000000000..3d87548483 --- /dev/null +++ b/libraries/esp8266/examples/DelegatePerf/Foo.h @@ -0,0 +1,20 @@ +/* + ESP8266 Delegate class template test by Dirk O. Kaar + This example code is in the public domain +*/ + +#include + +extern uint32_t cnt; + +struct Foo { + int val; + bool cb(int result) { + val = result; + ++cnt; + return true; + } + static bool cbwObj(Foo* obj, int result) { + return ((Foo*)obj)->cb(result); + } +}; \ No newline at end of file diff --git a/libraries/esp8266/examples/DelegatePerf/TestPrep.cpp b/libraries/esp8266/examples/DelegatePerf/TestPrep.cpp new file mode 100644 index 0000000000..4bf2e73895 --- /dev/null +++ b/libraries/esp8266/examples/DelegatePerf/TestPrep.cpp @@ -0,0 +1,85 @@ +/* + ESP8266 Delegate class template test by Dirk O. Kaar + This example code is in the public domain +*/ + +#include "Foo.h" +#ifndef NODELEGATE +#include +#else +#include +#endif +#include + +extern void stopwatch(); + +std::shared_ptr oPtr(new Foo()); +bool inline cbCPtr(int result) { + return oPtr->cb(result); +} + + +extern void set_f0(bool(*_f)(Foo*, int), Foo* _o); +extern void set_f1(bool(*_f)(Foo*, int), Foo* _o); +extern void set_f2(bool(*)(int result)); +extern void set_f3(bool(*)(int result)); + +extern void set_f4(const std::function& f); +extern void set_f5(const std::function& f); + +#ifndef NODELEGATE +extern void set_f6(const Delegate&); +extern void set_f7(const Delegate&); + +extern void set_f8(const Delegate&); + +extern void set_f9(const Delegate& f); +extern void set_f10(const Delegate& f); + +extern void set_f11(const Delegate& f); +extern void set_f12(const Delegate& f); +#else +extern void set_f6(const std::function&); +extern void set_f7(const std::function&); + +extern void set_f8(const std::function&); + +extern void set_f9(const std::function& f); +extern void set_f10(const std::function& f); + +extern void set_f11(const std::function& f); +extern void set_f12(const std::function& f); +#endif + +void testPrep() { + std::shared_ptr o(oPtr); + set_f0(Foo::cbwObj, o.get()); + set_f1([](Foo * o, int result) -> bool { return o->cb(result); }, o.get()); + set_f2(cbCPtr); + set_f3(cbCPtr); + + set_f4([o](int result) -> bool { return o->cb(result); }); + set_f5(std::bind(Foo::cbwObj, o.get(), std::placeholders::_1)); + + set_f6([o](int result) -> bool { return o->cb(result); }); + set_f7(std::bind(Foo::cbwObj, o.get(), std::placeholders::_1)); + +#ifndef NODELEGATE + // hint to compiler to generate Delegate constructor for simple fp instead of functional + using Fp2 = bool(*)(int); + set_f8(static_cast([](int result) -> bool { return cbCPtr(result); })); +#else + set_f8([](int result) -> bool { return cbCPtr(result); }); +#endif + + set_f9([o](int result) -> bool { return o->cb(result); }); + set_f10(std::bind(Foo::cbwObj, o.get(), std::placeholders::_1)); + +#ifndef NODELEGATE + set_f11({ [](Foo * o, int result) -> bool { return o->cb(result); }, o.get() }); // fast calling! + set_f12({ Foo::cbwObj, o.get() }); // fast calling! +#else + set_f11([o](int result) -> bool { return o->cb(result); }); + set_f12(std::bind(Foo::cbwObj, o.get(), std::placeholders::_1)); +#endif +}