251 lines
7.5 KiB
C++
251 lines
7.5 KiB
C++
#pragma once
|
|
|
|
#include <list>
|
|
#include <functional>
|
|
|
|
#include "util/Types.h"
|
|
|
|
/** Supporting functions and structs for EventEmitter. */
|
|
namespace detail {
|
|
|
|
/**
|
|
* Stores a listener function and a validity, when the listener is unbound,
|
|
* it will have the `valid` member set to false instead of removing it from the listeners map directly,
|
|
* to avoid iterator offset errors when listeners are removed during an emit.
|
|
* The invalidatable listener will then be removed next time an event is emitted.
|
|
*
|
|
* @tparam E - The event type that the listener is for.
|
|
*/
|
|
|
|
template<typename E>
|
|
struct InvalidatableListener {
|
|
InvalidatableListener(const typename E::function_type& listener): listener(listener) {}
|
|
|
|
/** The listener function. */
|
|
typename E::function_type listener;
|
|
|
|
/** Whether or not the function is still valid. */
|
|
bool valid = true;
|
|
};
|
|
|
|
/**
|
|
* A nasty recursive compile time function that takes a tuple of event types and
|
|
* generates a tuple of lists of pairs of event indices and maps (oh god).
|
|
* This type is then initialized to store the listeners on the EventEmitter.
|
|
*
|
|
* getEventListTuple(std::tuple<Event<A, ...>, Event<B, ...>) =
|
|
* std::tuple<
|
|
* std::pair<usize, std::unordered_map<usize, InvalidatableListener<Event<A, ...>>>>,
|
|
* std::pair<usize, std::unordered_map<usize, InvalidatableListener<Event<B, ...>>>>
|
|
* >
|
|
*
|
|
* @tparam E - The events tuple to generate the list off of.
|
|
* @returns a tuple type as specified above.
|
|
*/
|
|
|
|
template<typename E, usize N = 0, typename... Args>
|
|
inline constexpr auto getEventListTuple() {
|
|
if constexpr (N < std::tuple_size_v<E>) {
|
|
return getEventListTuple<E, N + 1, Args..., std::pair<usize,
|
|
std::unordered_map<usize, InvalidatableListener<
|
|
typename std::tuple_element_t<N, E>>>>>();
|
|
}
|
|
else {
|
|
return std::tuple<Args...>{};
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A reference to a listener added using `bind`.
|
|
* When all instances of it go out of scope, the listener
|
|
* that was bound to return this instance will be unbound from its emitter.
|
|
*/
|
|
|
|
class ListenerRef {
|
|
public:
|
|
ListenerRef() = default;
|
|
explicit ListenerRef(std::function<void()> unbind): unbind(make_shared<std::function<void()>>(unbind)) {};
|
|
~ListenerRef() { if (unbind && unbind.unique()) unbind->operator()(); }
|
|
|
|
private:
|
|
sptr<std::function<void()>> unbind = nullptr;
|
|
};
|
|
|
|
/**
|
|
* Specifies a single event of an EventEmitter, with an id and parameter list.
|
|
* Provided to a specialization of the EventEmitter class to specify its events.
|
|
*
|
|
* @tparam T - The integral identifier for the event.
|
|
* @tparam Args - The arguments that will be provided to listener functions.
|
|
*/
|
|
|
|
template <let T, typename... Args>
|
|
struct Event {
|
|
|
|
/** The integral identifier of the event as a u32. */
|
|
static constexpr u32 id_type = (u32)T;
|
|
|
|
/** A tuple of the event parameters. */
|
|
typedef std::tuple<Args...> args_type;
|
|
|
|
/** The type of the event listeners for this event. */
|
|
typedef std::function<void(Args...)> function_type;
|
|
};
|
|
|
|
/**
|
|
* Manages listeners for a set of events specified at compile time,
|
|
* allowing event listeners to be bound and events to be emitted at runtime.
|
|
* Each event may have its own parameter types. Objects that should emit events should
|
|
* derive from this class publicly, and make available an Event typedef that
|
|
* corresponds to the valid event identifiers provided to this class.
|
|
*
|
|
* @tparam E - The list of Events this listener should contain.
|
|
*/
|
|
|
|
template <typename... E>
|
|
class EventEmitter {
|
|
|
|
/** A tuple of the provided event types. */
|
|
typedef std::tuple<E...> event_types;
|
|
|
|
/** The type of the event list, which contains event listeners. */
|
|
typedef decltype(detail::getEventListTuple<event_types>()) event_list;
|
|
|
|
public:
|
|
|
|
/**
|
|
* Binds a listener to the specified event,
|
|
* which will be unbound when the ref goes out of scope.
|
|
*
|
|
* @tparam T - The event identifier to bind to.
|
|
* @param listener - The event listener, its parameters must match the event parameters.
|
|
* @returns a ListenerRef corresponding to the listener, which will unbind the listener when it goes out of scope.
|
|
*/
|
|
|
|
template <let T, typename L>
|
|
ListenerRef bind(const L& listener) {
|
|
usize ind = bindRaw<T>(listener);
|
|
return ListenerRef {[ind, this]() { unbind<T>(ind); }};
|
|
}
|
|
|
|
/**
|
|
* Binds a listener to the specified event, returning the raw listener id,
|
|
* which must be used to manually unbind the event later.
|
|
* Usually, `bind` should be called instead.
|
|
*
|
|
* @tparam T - The event identifier to bind to.
|
|
* @param listener - The event listener, its parameters must match the event parameters.
|
|
* @returns the listener's id, to be used in `unbind`.
|
|
*/
|
|
|
|
template <let T, typename L>
|
|
usize bindRaw(const L& listener) {
|
|
return addEventListener<T>(listener);
|
|
}
|
|
|
|
/**
|
|
* Unbinds the listener matching the ind from the specified event.
|
|
* Usually this won't need to be called, as ListenerRef handles it automatically.
|
|
*
|
|
* @tparam T - The event identifier to unbind the listener from.
|
|
* @param ind - The id of the bound listener.
|
|
*/
|
|
|
|
template <let T>
|
|
void unbind(usize ind) {
|
|
return removeEventListener<T>(ind);
|
|
}
|
|
|
|
protected:
|
|
|
|
/**
|
|
* Emits the specified event with the arguments provided.
|
|
* The arguments must match the Event specification.
|
|
*
|
|
* @tparam T - The event identifier to emit.
|
|
* @param args - The arguments to pass to the listener functions.
|
|
*/
|
|
|
|
template <let T, typename... Args>
|
|
void emit(const Args&... args) {
|
|
invokeEventListeners<T>(args...);
|
|
}
|
|
|
|
private:
|
|
|
|
/**
|
|
* Recursively finds the right list to add the specified
|
|
* listener to at compile time, adds it, and returns its index.
|
|
*
|
|
* @tparam T - The event identifier to bind to.
|
|
* @param listener - The listener function.
|
|
* @returns the index of the bound event listener.
|
|
*/
|
|
|
|
template<let T, usize N = 0, typename L>
|
|
inline usize addEventListener(const L& listener) {
|
|
if constexpr (std::tuple_element_t<N, event_types>::id_type == static_cast<u32>(T)) {
|
|
let& list = std::get<N>(eventList);
|
|
usize ind = list.first++;
|
|
list.second.emplace(ind, detail::InvalidatableListener<
|
|
std::tuple_element_t<N, event_types>> { listener });
|
|
return ind;
|
|
}
|
|
else {
|
|
return addEventListener<T, N + 1>(listener);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recursively finds the right list to remove the specified
|
|
* listener from at compile time, and marks it as invaid.
|
|
*
|
|
* @tparam T - The event identifier to unbind from.
|
|
* @param listener - The listener function.
|
|
*/
|
|
|
|
template<let T, usize N = 0>
|
|
inline void removeEventListener(const usize& ind) {
|
|
if constexpr (std::tuple_element_t<N, event_types>::id_type == static_cast<u32>(T)) {
|
|
let& list = std::get<N>(eventList).second;
|
|
list.at(ind).valid = false;
|
|
return;
|
|
}
|
|
else {
|
|
return removeEventListener<T, N + 1>(ind);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recursively finds the right list to invoke the listeners of,
|
|
* removes all invalid listeners, and invokes the remaining ones.
|
|
*
|
|
* @tparam T - The event identifier to invoke.
|
|
* @param args - The arguments to pass to the listener functions.
|
|
*/
|
|
|
|
template<let T, usize N = 0, typename... Args>
|
|
inline void invokeEventListeners(const Args&... args) {
|
|
if constexpr (std::tuple_element_t<N, event_types>::id_type == static_cast<u32>(T)) {
|
|
let& list = std::get<N>(eventList).second;
|
|
for (let it = list.begin(); it != list.end();) {
|
|
if (it->second.valid) {
|
|
it->second.listener(args...);
|
|
it++;
|
|
}
|
|
else {
|
|
it = list.erase(it);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else {
|
|
return invokeEventListeners<T, N + 1>(args...);
|
|
}
|
|
}
|
|
|
|
/** The list of event listeners. */
|
|
event_list eventList {};
|
|
};
|