diff --git a/deps/ox/src/ox/event/signal.cpp b/deps/ox/src/ox/event/signal.cpp index 25e0e300..946b5c24 100644 --- a/deps/ox/src/ox/event/signal.cpp +++ b/deps/ox/src/ox/event/signal.cpp @@ -10,6 +10,9 @@ namespace ox { +#ifndef OX_OS_BareMetal +template class Signal; +#endif template class Signal; SignalHandler::~SignalHandler() noexcept { diff --git a/deps/ox/src/ox/event/signal.hpp b/deps/ox/src/ox/event/signal.hpp index 9e023a4b..40cab5a9 100644 --- a/deps/ox/src/ox/event/signal.hpp +++ b/deps/ox/src/ox/event/signal.hpp @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -16,9 +17,206 @@ namespace ox { class SignalHandler; +#ifndef OX_OS_BareMetal + +template +struct isError { + static constexpr bool value = false; +}; + +template<> +struct isError { + static constexpr bool value = true; +}; + +template +class Signal { + private: + template + static constexpr void ignoreValue(const T&) {} + + protected: + struct BaseSlot { + virtual ~BaseSlot() = default; + virtual void call(Args...) = 0; + virtual void cleanup(Signal*) noexcept {} + virtual const void *receiver() noexcept { return nullptr; } + }; + + template + struct FunctionSlot: public BaseSlot { + RetT (*f)(Args...); + + explicit FunctionSlot(RetT (*f)(Args...)) { + this->f = f; + } + + void call(Args... args) final { + if constexpr(isError::value) { + oxThrowError(f(args...)); + } else { + f(args...); + } + } + }; + + template + struct MethodSlot: public BaseSlot { + T m_receiver = nullptr; + Method m_methodPtr; + + MethodSlot(T receiver, Method methodPtr) { + m_receiver = receiver; + m_methodPtr = methodPtr; + } + + void call(Args... args) final { + if constexpr(isError*(m_methodPtr))(args...))>::value) { + oxThrowError((m_receiver->*(m_methodPtr))(args...)); + } else { + f(args...); + } + } + + void cleanup(Signal *signal) noexcept final { + oxIgnoreError(m_receiver->destruction.disconnectSignal(signal)); + } + + const void *receiver() noexcept final { + return m_receiver; + } + }; + + template + struct SignalMethodSlot: public BaseSlot { + const SignalT *m_receiver = nullptr; + Method m_methodPtr; + + SignalMethodSlot(const SignalT *receiver, Method methodPtr) { + m_receiver = receiver; + m_methodPtr = methodPtr; + } + + void call(Args... args) final { + if constexpr(isError*(m_methodPtr))(args...))>::value) { + oxThrowError((m_receiver->*(m_methodPtr))(args...)); + } else { + (m_receiver->*(m_methodPtr))(args...); + } + } + + void cleanup(Signal*) noexcept final { + } + + const void *receiver() noexcept final { + return m_receiver; + } + }; + + mutable Vector> m_slots; + + public: + ~Signal() noexcept; + + void connect(Error(*f)(Args...)) const noexcept; + + template + void connect(const T *receiver, Method methodPtr) const noexcept; + + template + void connect(T *receiver, Method methodPtr) const noexcept; + + template + void connect(const Signal *receiver, Method methodPtr) const noexcept; + + template + Error disconnectSignal(const Signal *receiver) const noexcept; + + Error disconnectObject(const void *receiver) const noexcept; + + void emit(Args... args) const; + + Error emitCheckError(Args... args) noexcept; +}; + +extern template class Signal; + +template +Signal::~Signal() noexcept { + for (auto &slot : m_slots) { + slot->cleanup(this); + } +} + +template +void Signal::connect(Error(*f)(Args...)) const noexcept { + m_slots.emplace_back(new FunctionSlot(f)); +} + +template +template +void Signal::connect(const T *receiver, Method methodPtr) const noexcept { + receiver->destruction.connect(this, &Signal::disconnectObject); + m_slots.emplace_back(new MethodSlot(receiver, methodPtr)); +} + +template +template +void Signal::connect(T *receiver, Method methodPtr) const noexcept { + receiver->destruction.connect(this, &Signal::disconnectObject); + m_slots.emplace_back(new MethodSlot(receiver, methodPtr)); +} + +template +template +void Signal::connect(const Signal *receiver, Method methodPtr) const noexcept { + m_slots.emplace_back(new SignalMethodSlot, Method>(receiver, methodPtr)); +} + +template +template +Error Signal::disconnectSignal(const Signal *receiver) const noexcept { + return disconnectObject(receiver); +} + +template +Error Signal::disconnectObject(const void *receiver) const noexcept { + for (auto i = 0u; i < m_slots.size(); ++i) { + const auto &slot = m_slots[i]; + if (slot->receiver() == receiver) { + oxReturnError(m_slots.erase(i)); + --i; + } + } + return OxError(0); +} + +template +void Signal::emit(Args... args) const { + for (auto &f : m_slots) { + f->call(args...); + } +} + +template +Error Signal::emitCheckError(Args... args) noexcept { + try { + for (auto &f : m_slots) { + f->call(args...); + } + return OxError(0); + } catch (const ox::Exception &ex) { + return ox::Error(ex.file, ex.line, ex.errCode, ex.msg); + } +} + +#else + template class Signal; +#endif + template class Signal { protected: