C++ entity component system

The SIECS C++ API.

Ordinary C++ types and callback signatures map directly to the SIECS archetype runtime.

One storage model and one runtime

The C++ interface is defined in the public siecs.h header and calls the same C runtime used by the C API. Entities, archetype tables, queries, and systems therefore share one world.

The wrapper adds compile-time type information at the API boundary. Storage, matching, iteration, and scheduling remain runtime operations.

Use ordinary types as components

A component is a regular C++ type. The first typed operation registers its runtime component identifier in the active world.

#include <siecs.h>

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

int main() {
    ecs::init();

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

    if (player.has<Position, Velocity>()) {
        player.remove<Velocity>();
    }
}

Multi-component operations compute the final component set first. SIECS then places the entity in the destination archetype and writes each value into its component column.

Derive query access from the callback

Mutable references produce writable query terms. Const references produce read-only terms. Builder methods add filters such as required, excluded, and optional components.

ecs::query()
    .require<Visible>()
    .exclude<ecs::Disabled>()
    .each([](Position &position, const Velocity &velocity) {
        position.x += velocity.x;
        position.y += velocity.y;
    });

Once built, the query caches matching archetype tables and iterates their component columns through the callback.

Bind persistent queries to systems

A system stores the inferred query and callback in the world. ecs::progress() evaluates enabled systems according to their execution phase.

struct Time { float dt; };

ecs::set_resource(Time{ .dt = 0.016f });

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

Resource parameters access unique world-level values. They are resolved for the callback and remain separate from entity query terms.

Model entity targets with relations

Relations associate a relation kind with an entity target. Typed helpers use this model for hierarchy through ChildOf and shared prototype data through IsA.

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

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

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

Observers use the same typed callback model to react to component add, remove, and set events, as well as application-defined event types.