|
Toast Engine
|
Authors: Dante Harper
Created on: 29/April/2026
Updated on: 29/April/2026
To create a new event type, define a struct that inherits from event::Event<T>, where T is your event type. Event data must be defined in a constructor
Events are enqueued for dispatch using event::send<T>(...). This function is thread-safe. Arguments provided are forwarded to your event type's constructor.
Events sent during an event::pollEvents() call are queued and dispatched during the next event::pollEvents() invocation.
Callbacks are function objects invoked when an event is dispatched. The EventCallback concept supports various signatures:
Returning true from a callback consumes the event, stopping further propagation to other callbacks.
Callbacks can also be assigned a priority. Callbacks with a higher char priority value will be executed before those with lower priority. The default priority is 0.
The event::Listener class manages event subscriptions. When an event::Listener instance is destroyed, it automatically unsubscribes all registered callbacks. As well as enabling / disabling callbacks with void enabled(bool state)
You can subscribe callbacks using Listener::subscribe with the following signatures:
Explicitly unsubscribe named callbacks using Listener::unsubscribe<TEvent>(label) with the following signature:
void Listener::unsubscribe<TEvent>(std::string name)
Note: Unnamed callbacks are internally assigned the label "unnamed".
event::pollEvents() dispatches all currently queued events to their callbacks. This function is not thread-safe and must be called from a single, dedicated thread (e.g., the main thread).
As Explained above you must define an Event Type like this struct MyEvent : Event<MyEvent> {}
IEvent struct was made so that Event<T> would inherit and get a vtable for the notify function
*Note: In Debug this struct also holds a std::stackstrace for debugging support
In each event type created with Event<T> it creates a multimap callbacks list that maps every callback added to a priority (char)
When subscribing an callback we return a iterator_t of the inserted callback. This is because with a multimap we can assure that the iterator is not going to be invalidated
We can use the returned iterator_t to then unsubscribe the callback when we are ready
when we subscribe a function i use constexpr programming to convert the callback F&& to a (TEvent&)->bool callback and we add a check inside the callback to make sure the listener is enabled so that we can disable the callbacks if needed and then we store the iterator_t alongside a name and the a type_id of the event type
when we unsubscribe a function we can now search for every callback with the same name and type that was given and find and remove it properly from the list and in order to remove it properly from the callback list in Event<T> i made a sudo v-table to abstract the removal of callbacks
because of the fact that all events will be added and removed if FIFO order we can actually store all the events in 2 circular buffers (a type of memory pool search it up) and swap them every time we need to dispatch them
one circular buffer for adding new events and another for polling events in case we need to push new events on another thread while polling
when we call void pollEvents() we first delete the memory of all the callbacks added to the _detail::deletion_queue and swap the 2 circular buffers
then we iterator through each event and call its notify() function
then we clear the pollEvents memory pool
the notify() function first checks if any callbacks were added / removed from the callback list and if so we clear the cached list of callbacks and repopulate them with the new list. This allows us to modify the Master callback list during polling as well as increase cache locality a little bit.
then we iterator through the list of callbacks and if one returns true we return and dont propogate further