Skip to content

Application Module

The Application module serves as the entry point and lifecycle coordinator for Astra applications. It orchestrates the initialization of all other modules and manages the main application loop.

Purpose

The Application module provides:

  • A centralized entry point for Astra applications
  • Lifecycle management (initialization, start, run, shutdown)
  • Main loop coordination
  • Global singleton access throughout the engine

Key Components

Application Class

The Application class is a singleton created via a static factory method. It is not meant to be subclassed.

Public API:

  • static Application* init() - creates the instance and initializes all subsystems
  • void start() - activates runtime subsystems (window, engine, systems)
  • void run() - runs the main loop; blocks until window closes, then deletes the instance
  • static Application* get() - returns the singleton pointer
  • static void end() - explicit teardown (alternative to run() exit)

Architecture

Singleton Pattern

The Application uses a static factory (init()) to create the singleton. The constructor is private, preventing direct instantiation or subclassing:

cpp
namespace astralix {
class Application {
public:
    static Application* init();
    void start();
    void run();

    static Application* get();
    static void end();

private:
    Application() {};
    ~Application();

    static Application* m_instance;
};
}

Initialization Sequence

init() initializes subsystems in this order:

  1. EventDispatcher
  2. EventScheduler
  3. Time
  4. ProjectManager
  5. Engine
  6. SystemManager
  7. WindowManager

Start Sequence

start() activates the runtime subsystems after initialization:

  1. WindowManager::start() - opens the window
  2. Engine::start() - starts the ECS engine
  3. SystemManager::start() - starts all registered systems

Main Loop

run() executes the following each frame until the window closes:

while window is open:
    time->update()
    wm->update()
    scheduler POST_FRAME events
    system->fixed_update(1/60)
    system->update(deltaTime)
    scheduler IMMEDIATE events
    wm->swap()

// on exit: ~Application() ends Engine, Time, WindowManager

Shutdown

The destructor (~Application) handles cleanup automatically in this order:

  1. Engine::end()
  2. Time::end()
  3. WindowManager::end()

It is invoked either when run() exits (window closed) or when end() is called explicitly.

Usage Pattern

Creating an Application

cpp
#include "application.hpp"

using namespace astralix;

int main() {
    Application* app = Application::init();
    app->start();
    app->run(); // blocks until window closes, then cleans up
    return 0;
}

Accessing from Anywhere

cpp
Application* app = Application::get();

Explicit Teardown

For headless or non-loop scenarios, call end() instead of run():

cpp
Application* app = Application::init();
app->start();
// ... do work ...
Application::end();

File Locations

src/modules/application/
├── application.hpp    # Application class declaration
└── application.cpp    # Application implementation
  • Engine - initialized and torn down by Application
  • Window - managed via WindowManager
  • Project - managed via ProjectManager

Best Practices

  1. Call in order: always init()start()run(); do not skip steps
  2. Don't subclass: Application has no virtual hooks; extend behavior through systems and the ECS
  3. No manual shutdown needed: run() cleans up on exit; only use end() for non-loop scenarios
  4. Access via get(): use the singleton accessor rather than caching the pointer across frames