Events & signals
Decoupled communication lets one part of the program announce that something happened without knowing who cares.
The concept
Two common patterns: an event stream, where the platform produces a sequence of typed messages that code polls or receives, and signals and slots, where a producer exposes a typed signal and any number of listeners (slots) connect callbacks to it. Both remove direct dependencies between the sender and its receivers.
Why decouple?
If the player code calls the HUD directly, the player depends on the HUD, and the HUD cannot be removed or tested without it. With an announcement instead ("score changed"), the producer neither knows nor cares who listens: the HUD, an achievement tracker and a sound effect can all react, and none of them is named in the player code.
Polling versus callbacks
An event stream is usually pulled: the loop drains a queue once per frame and handles each message in order, so all reactions happen at a predictable point. A signal is pushed: the slots run immediately, inside the call that emitted it. Push is convenient but means arbitrary code runs in the middle of the emitter's work, so a slot that modifies the very collection being iterated can cause trouble.
Lifetimes
The classic bug of callbacks is the dangling listener: an object is destroyed but its callback is still registered, and the next emit calls into freed memory. Any signal design therefore needs a way to disconnect, either explicitly or automatically when the listener goes away.
How ASGE does it
System events
SDL events are translated once, in event::_internal::ProcessEvent, into
asge::event::SystemEvent: a std::variant over QuitEvent,
WindowEvent, KeyboardEvent, MouseMotionEvent, MouseButtonEvent,
MouseWheelEvent and UnknownEvent. Game code never sees SDL types; it asks for the case it cares
about with TryGet<EventT>(), which returns a pointer or null. Application forwards each event
to the input system and then to Game::OnSystemEvent.
void OnSystemEvent( asge::event::SystemEvent const& inSysEvent ) override
{
if ( auto const* key = inSysEvent.TryGet<asge::event::KeyboardEvent>() )
{
// react to the key press
}
}
Signals
asge::signals::Signal<Args...> is a thread-safe, type-checked signal: every slot must have the
signature of the signal. Connect() returns a Connection. A connection ends when you call
Disconnect() or when the optional lifetime token (a weak pointer you pass to Connect())
expires. Dead connections are pruned automatically on the next Emit().
asge::signals::Signal<int> onScore;
// Slot signature must match the signal's: void(int)
auto connection = onScore.Connect( []( int points ) { /* update HUD */ } );
onScore.Emit( 10 ); // runs every connected slot
connection.Disconnect(); // or pass a lifetime token to Connect() for automatic cleanup
Choosing
System events carry what the OS reports; signals are for your own code, for example a gameplay system announcing a score change to the HUD.
See also
Source: Events/Events.hpp, Core/Patterns/Signal.hpp.
Input demo