[ox/event] Add variant of Signal that uses exceptions
This commit is contained in:
parent
b61f7a95ad
commit
ad0d5c8caf
3
deps/ox/src/ox/event/signal.cpp
vendored
3
deps/ox/src/ox/event/signal.cpp
vendored
@ -10,6 +10,9 @@
|
||||
|
||||
namespace ox {
|
||||
|
||||
#ifndef OX_OS_BareMetal
|
||||
template class Signal<const SignalHandler*>;
|
||||
#endif
|
||||
template class Signal<Error(const SignalHandler*)>;
|
||||
|
||||
SignalHandler::~SignalHandler() noexcept {
|
||||
|
198
deps/ox/src/ox/event/signal.hpp
vendored
198
deps/ox/src/ox/event/signal.hpp
vendored
@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/defines.hpp>
|
||||
#include <ox/std/error.hpp>
|
||||
#include <ox/std/memory.hpp>
|
||||
#include <ox/std/vector.hpp>
|
||||
@ -16,9 +17,206 @@ namespace ox {
|
||||
|
||||
class SignalHandler;
|
||||
|
||||
#ifndef OX_OS_BareMetal
|
||||
|
||||
template<typename T>
|
||||
struct isError {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct isError<Error> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<class... Args>
|
||||
class Signal {
|
||||
private:
|
||||
template<typename T>
|
||||
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<typename RetT>
|
||||
struct FunctionSlot: public BaseSlot {
|
||||
RetT (*f)(Args...);
|
||||
|
||||
explicit FunctionSlot(RetT (*f)(Args...)) {
|
||||
this->f = f;
|
||||
}
|
||||
|
||||
void call(Args... args) final {
|
||||
if constexpr(isError<decltype(f(args...))>::value) {
|
||||
oxThrowError(f(args...));
|
||||
} else {
|
||||
f(args...);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename Method>
|
||||
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<decltype((m_receiver->*(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<typename SignalT, typename Method>
|
||||
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<decltype((m_receiver->*(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<UniquePtr<BaseSlot>> m_slots;
|
||||
|
||||
public:
|
||||
~Signal() noexcept;
|
||||
|
||||
void connect(Error(*f)(Args...)) const noexcept;
|
||||
|
||||
template<typename T, typename Method>
|
||||
void connect(const T *receiver, Method methodPtr) const noexcept;
|
||||
|
||||
template<typename T, typename Method>
|
||||
void connect(T *receiver, Method methodPtr) const noexcept;
|
||||
|
||||
template<class... SubArgs, typename Method>
|
||||
void connect(const Signal<SubArgs...> *receiver, Method methodPtr) const noexcept;
|
||||
|
||||
template<class... SubArgs>
|
||||
Error disconnectSignal(const Signal<SubArgs...> *receiver) const noexcept;
|
||||
|
||||
Error disconnectObject(const void *receiver) const noexcept;
|
||||
|
||||
void emit(Args... args) const;
|
||||
|
||||
Error emitCheckError(Args... args) noexcept;
|
||||
};
|
||||
|
||||
extern template class Signal<const SignalHandler*>;
|
||||
|
||||
template<class... Args>
|
||||
Signal<Args...>::~Signal() noexcept {
|
||||
for (auto &slot : m_slots) {
|
||||
slot->cleanup(this);
|
||||
}
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void Signal<Args...>::connect(Error(*f)(Args...)) const noexcept {
|
||||
m_slots.emplace_back(new FunctionSlot(f));
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
template<typename T, typename Method>
|
||||
void Signal<Args...>::connect(const T *receiver, Method methodPtr) const noexcept {
|
||||
receiver->destruction.connect(this, &Signal<Error(Args...)>::disconnectObject);
|
||||
m_slots.emplace_back(new MethodSlot<const T*, Method>(receiver, methodPtr));
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
template<typename T, typename Method>
|
||||
void Signal<Args...>::connect(T *receiver, Method methodPtr) const noexcept {
|
||||
receiver->destruction.connect(this, &Signal<Error(Args...)>::disconnectObject);
|
||||
m_slots.emplace_back(new MethodSlot<T*, Method>(receiver, methodPtr));
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
template<class... SubArgs, typename Method>
|
||||
void Signal<Args...>::connect(const Signal<SubArgs...> *receiver, Method methodPtr) const noexcept {
|
||||
m_slots.emplace_back(new SignalMethodSlot<Signal<SubArgs...>, Method>(receiver, methodPtr));
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
template<class... SubArgs>
|
||||
Error Signal<Args...>::disconnectSignal(const Signal<SubArgs...> *receiver) const noexcept {
|
||||
return disconnectObject(receiver);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
Error Signal<Args...>::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<class... Args>
|
||||
void Signal<Args...>::emit(Args... args) const {
|
||||
for (auto &f : m_slots) {
|
||||
f->call(args...);
|
||||
}
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
Error Signal<Args...>::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<typename T>
|
||||
class Signal;
|
||||
|
||||
#endif
|
||||
|
||||
template<class... Args>
|
||||
class Signal<Error(Args...)> {
|
||||
protected:
|
||||
|
Loading…
Reference in New Issue
Block a user