/* * Copyright 2015 - 2025 gary@drinkingtea.net * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #pragma once #include #include #include #include #include #include #include namespace ox { class SignalHandler; #ifndef OX_OS_BareMetal namespace detail { template struct isError { static constexpr bool value = false; }; template<> struct isError { static constexpr bool value = true; }; } template class Signal { protected: struct BaseSlot { virtual ~BaseSlot() = default; virtual void call(Args...) = 0; virtual void cleanup(Signal*) noexcept {} [[nodiscard]] virtual const void *receiver() const noexcept { return nullptr; } }; template struct FunctionSlot: public BaseSlot { F f; explicit FunctionSlot(F f) { this->f = f; } void call(Args... args) final { if constexpr(detail::isError::value) { OX_THROW_ERROR(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(detail::isError*(m_methodPtr))(args...))>::value) { OX_THROW_ERROR((m_receiver->*(m_methodPtr))(args...)); } else { f(args...); } } void cleanup(Signal *signal) noexcept final { std::ignore = m_receiver->destruction.disconnectSignal(signal); //if (err) { // oxErrorf("Signal could not notify receiver that it is being destroyed. Destruction of receiver will cause use-after-free. ({})", toStr(err)); //} } [[nodiscard]] const void *receiver() const 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(detail::isError*(m_methodPtr))(args...))>::value) { OX_THROW_ERROR((m_receiver->*(m_methodPtr))(args...)); } else { (m_receiver->*(m_methodPtr))(args...); } } void cleanup(Signal*) noexcept final { } [[nodiscard]] const void *receiver() const 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) const 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) { OX_RETURN_ERROR(m_slots.erase(i)); --i; } } return ox::Error(1, "Signal::disconnectObject: Receiver was not found among this Signal's slots"); } template void Signal::emit(Args... args) const { for (auto &f : m_slots) { f->call(args...); } } template Error Signal::emitCheckError(Args... args) const noexcept { try { for (auto &f : m_slots) { f->call(args...); } return ox::Error(0); } catch (const ox::Exception &ex) { return ox::Error(ex.errCode, ex.msg, ex.src); } } #else template class Signal; #endif template class Signal { protected: struct BaseSlot { virtual ~BaseSlot() = default; virtual Error call(Args...) noexcept = 0; virtual void cleanup(Signal*) noexcept {} [[nodiscard]] virtual const void *receiver() const noexcept { return nullptr; } }; struct FunctionSlot: public BaseSlot { Error (*f)(Args...); explicit FunctionSlot(Error (*f)(Args...)) noexcept { this->f = f; } Error call(Args... args) noexcept final { return f(ox::forward(args)...); } }; template struct MethodSlot: public BaseSlot { T m_receiver = nullptr; Method m_methodPtr; MethodSlot(T receiver, Method methodPtr) { m_receiver = receiver; m_methodPtr = methodPtr; } Error call(Args... args) noexcept final { return (m_receiver->*(m_methodPtr))(ox::forward(args)...); } void cleanup(Signal *signal) noexcept final { std::ignore = m_receiver->destruction.disconnectSignal(signal); //oxErrorf("{}", toStr(err)); //oxAssert(err, "Signal could not notify receiver that it is being destroyed. Destruction of receiver will cause use-after-free."); } [[nodiscard]] const void *receiver() const 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; } Error call(Args... args) noexcept final { return (m_receiver->*(m_methodPtr))(ox::forward(args)...); } void cleanup(Signal*) noexcept final { } [[nodiscard]] const void *receiver() const 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 noexcept; Error emitCheckError(Args... args) const noexcept; }; extern template class Signal; class SignalHandler { public: Signal destruction; constexpr SignalHandler() noexcept = default; SignalHandler(const SignalHandler&) = delete; SignalHandler(SignalHandler&) = delete; SignalHandler(SignalHandler&&) = delete; virtual ~SignalHandler() noexcept; }; 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) { OX_RETURN_ERROR(m_slots.erase(i)); --i; } } return ox::Error(1, "Signal::disconnectObject: Receiver was not found among this Signal's slots"); } template void Signal::emit(Args... args) const noexcept { for (auto &f : m_slots) { std::ignore = f->call(ox::forward(args)...); } } template Error Signal::emitCheckError(Args... args) const noexcept { for (auto &f : m_slots) { OX_RETURN_ERROR(f->call(ox::forward(args)...)); } return ox::Error(0); } }