Files
Destrum/destrum/include/destrum/Event.h
T

129 lines
3.6 KiB
C++

#ifndef EVENT_H
#define EVENT_H
#include <functional>
#include <cstdint>
#include <unordered_set>
#include <vector>
class EventListener;
class BaseEvent {
public:
BaseEvent() = default;
virtual ~BaseEvent() = default;
virtual void RemoveListener(EventListener* listener) = 0;
};
class EventListener {
template <typename... EventArgs>
friend class Event;
public:
virtual ~EventListener() {
for (auto* event: m_Events)
event->RemoveListener(this);
}
EventListener(EventListener&&) = delete;
EventListener(const EventListener&) = delete;
EventListener& operator=(EventListener&&) = delete;
EventListener& operator=(const EventListener&) = delete;
protected:
EventListener() = default;
private:
void AddEvent(BaseEvent* event) { m_Events.insert(event); }
void RemoveEvent(BaseEvent* event) { m_Events.erase(event); }
std::unordered_set<BaseEvent*> m_Events{};
};
template <typename... EventArgs>
class Event final: public BaseEvent {
struct EventFunction {
void* listener{nullptr};
std::uint64_t id{0};
std::function<void(EventArgs...)> callback;
};
public:
Event() = default;
~Event() override {
for (auto* eventListener: m_EventListeners)
eventListener->RemoveEvent(this);
}
Event(Event&&) = delete;
Event(const Event&) = delete;
Event& operator=(Event&&) = delete;
Event& operator=(const Event&) = delete;
//This is to allow any member function of a EventListener to be bound as a event callback
template <typename ObjectType>
requires std::derived_from<ObjectType, EventListener>
void AddListener(ObjectType* object, void (ObjectType::*memberFunction)(EventArgs...)) {
auto* listener = static_cast<EventListener*>(object);
listener->AddEvent(this);
m_EventListeners.insert(listener);
const auto id = m_NextBindingId++;
m_ActiveBindings.insert(id);
m_FunctionBinds.push_back({
listener,
id,
[object, memberFunction] (EventArgs... args) { (object->*memberFunction)(args...); }
});
}
template <typename Function>
void AddListener(Function function) {
const auto id = m_NextBindingId++;
m_ActiveBindings.insert(id);
m_FunctionBinds.push_back({
nullptr,
id,
[function] (EventArgs... args) { function(args...); }
});
}
template <typename... Args>
void Invoke(Args&&... args) {
// Callbacks are allowed to unregister themselves or register another
// callback. Iterate over a snapshot so those mutations cannot
// invalidate this invocation.
const auto listeners = m_FunctionBinds;
for (const auto& listenerFunction: listeners) {
if (m_ActiveBindings.contains(listenerFunction.id)) {
listenerFunction.callback(args...);
}
}
}
void RemoveListener(EventListener* listener) override {
m_EventListeners.erase(listener);
for (auto it = m_FunctionBinds.begin(); it != m_FunctionBinds.end();) {
if (it->listener == static_cast<void*>(listener)) {
m_ActiveBindings.erase(it->id);
it = m_FunctionBinds.erase(it);
} else {
++it;
}
}
}
private:
std::vector<EventFunction> m_FunctionBinds{};
std::unordered_set<EventListener*> m_EventListeners{};
std::unordered_set<std::uint64_t> m_ActiveBindings{};
std::uint64_t m_NextBindingId{1};
};
#endif //EVENT_H