C++ entity component system

A typed C++ API for data-oriented code.

Use ordinary structs as components, let callback parameters describe access, and keep systems readable as the application grows.

A complete C++ program

The API keeps component data, world state, and system access close to the code that uses them.

#include <siecs.h>

struct Position { float x, y; };
struct Velocity { float x, y; };
struct Time { float dt; };

int main() {
    ecs::init();
    ecs::set_resource(Time{ .dt = 1.0f / 60.0f });

    ecs::entity::create("player").set(
        Position{ .x = 0.0f, .y = 0.0f },
        Velocity{ .x = 1.0f, .y = 1.0f }
    );

    ecs::system("Move")
        .phase(EcsOnUpdate)
        .each([](
            ecs::res<const Time> time,
            Position &position,
            const Velocity &velocity
        ) {
            position.x += velocity.x * time->dt;
            position.y += velocity.y * time->dt;
        });

    ecs::progress();
    ecs::fini();
}

Use ordinary C++ types

Components are regular structs. SIECS associates each type with the active world when it first appears in a typed operation, then stores its values in the component data model.

Multi-component set, add, and remove calls express a final entity shape in one operation. This keeps entity construction concise and makes composition changes easy to read.

Let the callback describe access

A mutable reference requests writable data. A const reference requests read-only data. Resource parameters provide unique world state without becoming entity query terms.

ecs::system("Move").each([](
    ecs::res<const Time> time,
    Position &position,
    const Velocity &velocity
) {
    position.x += velocity.x * time->dt;
});

Query matching and batch iteration remain part of the SIECS runtime, while the C++ layer keeps the access contract visible in the function signature.

Compose the world with typed APIs

Systems, resources, observers, and modules share the same world as entity data. Modules can register related components, systems, and observers behind one typed import and can be enabled or disabled as a unit.

Reflection metadata can describe component fields for JSON serialization and live editor tooling through the optional REST explorer.

Model relationships directly

Relations give entities named targets. Use them for scene hierarchy, ownership, attachments, or reusable prototypes with shared component data.

auto parent = ecs::entity::create("ship");
auto child = ecs::entity::create("turret").child_of(parent);

auto prototype = ecs::entity::create("Enemy");
prototype.set(Health{ 100 }).abstract();

auto instance = ecs::entity::create("drone").is_a(prototype);

Relation state remains part of the world data and can be matched by queries, observed, reflected, and inspected by tools.

Link the C runtime with your C++ application

Include siecs.h, compile the SIECS source as C, and link the object with the C++ application.

cc -std=c23 -I. -c siecs.c -o siecs.o
c++ -std=c++23 -I. main.cpp siecs.o -pthread -o ecs_example

Continue with the C++ quick start and API reference for the complete set of typed operations.