#pragma once #include #include #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 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) = * std::tuple< * std::pair>>>, * std::pair>>> * > * * @tparam E - The events tuple to generate the list off of. * @returns a tuple type as specified above. */ template inline constexpr auto getEventListTuple() { if constexpr (N < std::tuple_size_v) { return getEventListTuple>>>>(); } else { return std::tuple{}; } } } /** * 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 unbind): unbind(make_shared>(unbind)) {}; ~ListenerRef() { if (unbind && unbind.unique()) unbind->operator()(); } private: sptr> 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 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_type; /** The type of the event listeners for this event. */ typedef std::function 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 class EventEmitter { /** A tuple of the provided event types. */ typedef std::tuple event_types; /** The type of the event list, which contains event listeners. */ typedef decltype(detail::getEventListTuple()) 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 ListenerRef bind(const L& listener) { usize ind = bindRaw(listener); return ListenerRef {[ind, this]() { unbind(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 usize bindRaw(const L& listener) { return addEventListener(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 void unbind(usize ind) { return removeEventListener(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 void emit(const Args&... args) { invokeEventListeners(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 inline usize addEventListener(const L& listener) { if constexpr (std::tuple_element_t::id_type == static_cast(T)) { let& list = std::get(eventList); usize ind = list.first++; list.second.emplace(ind, detail::InvalidatableListener< std::tuple_element_t> { listener }); return ind; } else { return addEventListener(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 inline void removeEventListener(const usize& ind) { if constexpr (std::tuple_element_t::id_type == static_cast(T)) { let& list = std::get(eventList).second; list.at(ind).valid = false; return; } else { return removeEventListener(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 inline void invokeEventListeners(const Args&... args) { if constexpr (std::tuple_element_t::id_type == static_cast(T)) { let& list = std::get(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(args...); } } /** The list of event listeners. */ event_list eventList {}; };