Class ModuleStack

Class Documentation

class ModuleStack

Dependency injection container managing module lifetime and execution.

ModuleStack is the central registry for all modules in a Portal application. It handles module registration, dependency resolution through topological sorting, and provides lifecycle facade methods that execute modules in dependency order.

After modules are registered with add_module(), call build_dependency_graph() to perform topological sorting. This organizes modules by dependency level and creates pre-sorted vectors for each tag type.

The lifecycle facade methods (begin_frame, update, etc.) iterate the appropriate tag-specific vectors, ensuring modules execute in dependency order with zero runtime overhead for resolution.

Example usage:

ModuleStack modules;
modules.add_module<Renderer>(STRING_ID("Renderer"));
modules.add_module<ResourceRegistry>(STRING_ID("Resources"));
modules.add_module<GameLogic>(STRING_ID("Game"));
modules.build_dependency_graph();  // Must call after registration

// In game loop:
// Calls update() on modules in dependency order
modules.update(frame_context);

Public Functions

~ModuleStack()
void clean()

Clean up all modules in reverse dependency order. Called by destructor, but can be called manually for explicit cleanup.

template<typename T, typename ...Args>
inline T &add_module(Args&&... args)

Register a new module and construct it in-place.

The module receives a reference to this ModuleStack as its first constructor argument, followed by any additional arguments. During construction, the module’s dependencies are resolved using ModuleLookup.

Marks the dependency graph as dirty, requiring rebuild before execution.

Template Parameters:
  • T – The module type, must inherit from BaseModule

  • Args – Additional constructor argument types

Parameters:

args – Arguments forwarded to the module’s constructor (after ModuleStack&)

Throws:

std::runtime_error – if module dependencies cannot be resolved

Returns:

Reference to the newly constructed module

inline std::span<std::unique_ptr<BaseModule>> list_modules()

Get a span of all registered modules for introspection.

Returns:

Span of unique pointers to all registered modules

void build_dependency_graph()

Perform topological sort of modules based on dependencies.

This method organizes modules into dependency levels using a depth-first search algorithm. Modules with no dependencies are level 0, and each module’s level is one plus the maximum level of its dependencies.

The sorted modules are organized into:

  • dependency_graph: vector-of-vectors where each inner vector is a dependency level

  • Tag-specific vectors (update_modules, etc.): pre-sorted for efficient iteration

Must be called after module registration and before execution.

Throws:

std::runtime_error – if circular dependencies are detected

void begin_frame(FrameContext &frame) const

Call begin_frame() on all FrameLifecycle modules in dependency order.

Parameters:

frame – Per-frame context data

void end_frame(FrameContext &frame) const

Call end_frame() on all FrameLifecycle modules in reverse dependency order.

Parameters:

frame – Per-frame context data

void update(FrameContext &frame) const

Call update() on all Update-tagged modules in dependency order.

Parameters:

frame – Per-frame context data

void post_update(FrameContext &frame) const

Call post_update() on all PostUpdate-tagged modules in dependency order.

Parameters:

frame – Per-frame context data

void gui_update(FrameContext &frame) const

Call gui_update() on all GuiUpdate-tagged modules in dependency order.

Parameters:

frame – Per-frame context data

void on_event(Event &event) const

Call on_event() on all Event-tagged modules.

Parameters:

event – The event to dispatch

inline const std::vector<std::vector<BaseModule*>> &get_dependency_graph() const

Get the computed dependency graph for introspection.

Returns:

Vector of dependency levels, where each level is a vector of modules