Extra2D/include/core/event/event_bus.h

178 lines
4.2 KiB
C++

#pragma once
#include <types/base/types.h>
#include <functional>
#include <tuple>
#include <vector>
#include <cstddef>
namespace extra2d::event {
/**
* @brief 监听器链表节点
*/
struct ListenerEntry {
ListenerEntry* next = nullptr;
ListenerEntry* prev = nullptr;
class ListenerBase* listener = nullptr;
};
/**
* @brief 监听器基类
*/
class ListenerBase {
protected:
ListenerEntry* entry_ = nullptr;
friend class ListenerContainer;
};
/**
* @brief 监听器容器,管理同一事件的所有监听器
*/
class ListenerContainer {
public:
ListenerContainer() = default;
~ListenerContainer();
template<typename EHandler, typename... Args>
bool broadcast(Args&&... args);
protected:
ListenerEntry* listenerList_ = nullptr;
ListenerEntry* listenersToAdd_ = nullptr;
std::vector<ListenerEntry*> listenersToRemove_;
int broadcasting_ = 0;
void addListener(ListenerBase* listener);
void removeListener(ListenerBase* listener);
void processPendingListeners();
bool hasPendingListeners() const;
template<typename EHandler>
friend class Listener;
};
/**
* @brief 事件处理器数据库,每个事件类型一个容器
*/
template<typename EHandler>
class ListenerDB {
public:
static ListenerContainer* container() {
static ListenerContainer* ctn = new ListenerContainer();
return ctn;
}
};
/**
* @brief 事件特征定义
*/
template<typename BusT, typename... Args>
struct EventTrait {
using Bus = BusT;
using ArgTuple = std::tuple<Args...>;
static constexpr size_t ARG_COUNT = sizeof...(Args);
};
/**
* @brief 元组提取器
*/
template<typename T>
struct TupleExtractor {
using FuncType = void();
using StdFuncType = std::function<void()>;
};
template<typename... Args>
struct TupleExtractor<std::tuple<Args...>> {
using FuncType = void(Args...);
using StdFuncType = std::function<void(Args...)>;
};
/**
* @brief 监听器模板类
*
* 构造时自动注册,析构时自动注销
*/
template<typename EHandler>
class Listener : public ListenerBase {
public:
using ArgTuple = typename EHandler::ArgTuple;
using StdFuncType = typename TupleExtractor<ArgTuple>::StdFuncType;
Listener();
~Listener();
template<typename Fn>
void bind(Fn&& func) {
callback_ = std::forward<Fn>(func);
}
void enable() { enabled_ = true; }
void disable() { enabled_ = false; }
bool isEnabled() const { return enabled_; }
void reset() { callback_ = nullptr; }
template<typename... Args>
void invoke(Args&&... args) {
if (callback_ && enabled_) {
callback_(std::forward<Args>(args)...);
}
}
const char* busName() const { return EHandler::BUS_NAME; }
const char* eventName() const { return EHandler::NAME; }
private:
bool enabled_ = true;
StdFuncType callback_;
};
template<typename EHandler>
Listener<EHandler>::Listener() {
entry_ = new ListenerEntry();
entry_->listener = this;
ListenerDB<EHandler>::container()->addListener(this);
}
template<typename EHandler>
Listener<EHandler>::~Listener() {
ListenerDB<EHandler>::container()->removeListener(this);
}
#define EVENT_LIST_LOOP_BEGIN(curr, list) \
for (ListenerEntry* curr = list; curr != nullptr; curr = curr->next) {
#define EVENT_LIST_LOOP_END(curr, list) }
template<typename EHandler, typename... Args>
bool ListenerContainer::broadcast(Args&&... args) {
broadcasting_++;
EVENT_LIST_LOOP_BEGIN(curr, listenerList_)
if (curr->listener) {
static_cast<Listener<EHandler>*>(curr->listener)->invoke(std::forward<Args>(args)...);
}
EVENT_LIST_LOOP_END(curr, listenerList_)
broadcasting_--;
if (!broadcasting_ && hasPendingListeners()) {
processPendingListeners();
}
return true;
}
/**
* @brief 广播事件
*/
template<typename EHandler, typename... Args>
void broadcast(Args&&... args) {
static_assert(sizeof...(Args) == EHandler::ARG_COUNT, "Parameter count incorrect");
auto* container = ListenerDB<EHandler>::container();
container->template broadcast<EHandler, Args...>(std::forward<Args>(args)...);
}
} // namespace extra2d::event