Skip to content

Static Event System

This module is a generation-driven event system.

The main goal is to keep user-facing APIs small and clear while moving infrastructure complexity to generated code and core internals.

Why this approach

Compared to runtime-only registration models, this design gives: - Stronger compile-time contracts for handlers. - Predictable startup semantics: system starts processing only after all expected handlers are registered. - Cleaner user includes: user code mostly consumes system/EventSenders.hpp, generated handler interfaces, and payload types.

High-Level Flow

  1. JSON configs define event ids, payload types, queues, and handler subscriptions.
  2. Python generators render C++ headers/sources from templates.
  3. User handler classes inherit generated interfaces and implement typed handleEvent... methods.
  4. User handlers self-register/unregister via helper API.
  5. EventSystem routes events through queue + dispatcher to registered handlers.

Core Components

This type of system has some similarities with Dynamic Event System

  • EventSystem
  • Public facade and lifecycle owner.
  • Coordinates dispatcher, queue, and registration tracker.

  • EventQueue

  • Worker-thread queue and dispatch loop.
  • Stores last event payload per event type.

  • EventDispatcher

  • Maps event id -> list of handler ids.
  • Calls handler processEvent(...) if registered.

  • RegistrationTracker

  • Tracks which generated handlers are registered.
  • Declares allRegistered() and provides watchdog logging for missing handlers.

  • Generated interfaces (generated/include/handlers/*)

  • One interface per configured handler.
  • Each interface has typed handleEvent... methods.

  • Generated send API (generated/include/system/EventSenders.hpp)

  • User-oriented event send helpers.
  • Hides payload erasure details.

Include Surface Layout

  • User-oriented
  • event_system/generated/include/system/EventSenders.hpp
  • event_system/include/system/EventSystem.hpp
  • event_system/generated/include/handlers/*
  • event_system/generated/include/types/*

  • Core/internal

  • event_system/include/core/*
  • event_system/generated/include/core/*

Only EventSenders stays in generated system include folder. Core ids and helpers are intentionally moved under core.

Config Files and Why They Matter

Config directory: event_system/configs

  • events_enum.json
  • Numeric event ids.
  • Source of generated enums for event ids.

  • events_types.json

  • Payload type model and event -> payload mapping.
  • Drives generated payload type headers and sender/handler signatures.

  • event_handlers.json

  • Handler names, subscriptions, queue assignments.
  • Drives generated handler interfaces and event-to-handler map builder.

Benefits of config-first setup:

  • Single source of truth for event contracts.
  • Repeatable generation with low manual drift.
  • Easier review of API changes through JSON diffs.

How to Implement Handlers

  1. Generate interfaces.
  2. In app/include/app/handlers, create concrete classes inheriting generated interfaces.
  3. Implement all required typed handleEvent... methods.
  4. Register in constructor, unregister in destructor.
  5. Instantiate handlers in your top-level App object so their lifetime is process-scoped.

Typical pattern:

class EventHandler1 final : protected event_system::IEventHandler_1
{
public:
    EventHandler1();
    ~EventHandler1();

private:
    void handleEventTimestamp(const event_system::Timestamp& payload) override;
    void handleEventEventSystemReady() override;
};

How to Generate Interfaces and Sources

From event_system directory:

python3 scripts/generate_all.py --root .

Or individually:

python3 scripts/generate_static_headers.py --root .
python3 scripts/generate_event_handlers_map_builder.py --root .
python3 scripts/generate_event_handlers.py --root .

Build and Run

From StaticEventSystem directory:

cmake -S . -B build
cmake --build build
./build/app/test_app

Stop with Ctrl+C.

Architecture

Class Diagram

class diagram

Sequence Diagram

sequence diagram

Positive Sides

  • Strong typed handler contracts.
  • Deterministic startup gate through registration completeness.
  • Cleaner user-facing include suggestions.
  • Config-driven API evolution.

Negative Sides / Risks

  • Generation pipeline adds tooling complexity.
  • Bad templates can break many generated files at once.
  • Concurrency-sensitive internals still need careful hardening.
  • Refactors require synchronized changes in scripts + templates + runtime.

Can it be better?

Yes. Future improvements:

  • Generate more runtime internals (queue and registration policy) from config.
  • Add schema validation for config files before generation.
  • Add CI checks to enforce generator determinism and compile outputs.
  • Introduce integration tests for startup/shutdown and unregister semantics.