text// Internal structure
include/darkmoon/ecs/
    ├── entity_manager.hpp   ← EntityManager<CMPList, TAGList>
    ├── slotmap.hpp          ← Slotmap<T, Capacity><T> (component storage)
    └── meta_program.hpp     ← MP:: namespace (TypeList, traits, masks)

// User structure
game/src/
    ├── utils/
    │   └── types.hpp        ← user defines CMPList, TAGList, using EM = ...
    ├── components/
    │   ├── render_component.hpp
    │   └── physics_component.hpp
    └── systems/
        ├── render_system.hpp
        └── physics_system.hpp

texttypes.hpp defines CMPList, TAGList, EM = EntityManager<CMPList, TAGList>
            │
            ▼
        user owns EM m_em {}
            │
            ├── m_em.newEntity()        --> Entity&
            ├── m_em.addComponent(e)    --> T&
            ├── m_em.getComponent(e)    --> T&
            └── m_em.forEach()          --> lambda(Entity&, T&...)

types.hpp

c++#pragma once
#include <darkmoon/ecs.hpp>

// Include your component headers
#include "../components/render_component.hpp"
#include "../components/physics_component.hpp"

// List all components here (order matters for the bitmask)
using GameCMPs = MP::TypeList<
    RenderComponent,
    PhysicsComponent
>;

// Tags: empty structs used as boolean flags, no data, no SlotMap slot
struct TagPlayer {};
struct TagEnemy  {};

// List all tags here
using GameTAGs = MP::TypeList<
    TagPlayer,
    TagEnemy
>;

// Instantiate the EntityManager with your types
using EM     = EntityManager<GameCMPs, GameTAGs>;
using Entity = EM::Entity;

C++// components/physics_component.hpp
#pragma once

struct PhysicsComponent {
    int positionX {}, positionY {};
};

// components/render_component.hpp
#pragma once

struct RenderComponent {
    char character {};
    RenderComponent(const char* ch) : character(*ch) {}
};

// Adding a component to an entity
auto& e = EM.newEntity();
EM.addComponent<PhysicsComponent>(e);

// Checking a component in a system
if (e.hasComponent<PhysicsComponent>()) { /* ... */ }

// Iterating only entities with a specific component
EM.forEach<MP::TypeList<PhysicsComponent>, MP::TypeList<>>([&](Entity& e){
    // only PhysicsComponent entities
});

C++// In types.hpp
struct TagPlayer {};
struct TagEnemy  {};

using GameTAGs = MP::TypeList<TagPlayer, TagEnemy>;

// Adding a tag to an entity
auto& e = EM.newEntity();
EM.addTag<TagPlayer>(e);

// Checking a tag in a system
if (e.hasTag<TagPlayer>()) { /* ... */ }

// Iterating only entities with a specific tag
EM.forEach<MP::TypeList<>, MP::TypeList<TagPlayer>>([&](Entity& e){
    // only TagPlayer entities
});

C++// systems/render_system.hpp
#pragma once
#include "../utils/types.hpp"

struct RenderSystem {
    // Declare which components and tags this system needs
    using SYSCMPs = MP::TypeList<RenderComponent>;
    using SYSTAGs = MP::TypeList<>;

    void update(EM& em) {
        em.forEach<SYSCMPs, SYSTAGs>([&](Entity& e, RenderComponent& r) {
            // Optionally access other components on the same entity
            if (e.hasComponent<PhysicsComponent>()) {
                auto& phys = em.getComponent<PhysicsComponent>(e);
                // do something
            }
            std::cout << r.character << "\n";
        });
    }
};

// main.cpp
#include "./systems/render_system.hpp"

int main() {
    EM em {};

    Entity& e = em.newEntity();
    em.addComponent<RenderComponent>(e);

    RenderSystem rm {};

    while (true) {
        rm.update(em);
    }
}

newEntity() Entity&
destroyEntityByID(id) void
destroyEntities(deathSet) void
destroyAll() void
getEntityByID(id) Entity*
getEntities() std::span<Entity>
aliveCount() std::size_t
freeEntities() std::size_t

getID() std::size_t
hasComponent<T>() bool
hasTag<T>() bool

addComponent<T>(e, args...) T&
getComponent<T>(e) T&
destroyComponent<T>(e) void
addTag<T>(e) void
destroyTag<T>(e) void

forEach<CMPs, TAGs>(func) void
forEachAny<CMPs, TAGs>(func) void
C++// Safe pattern for destroying entities during iteration
EM::deathSet toDestroy {};

EM.forEach<SYSCMPs, SYSTAGs>([&](Entity& e, PhysicsComponent& p) {
    if (p.positionX > 100)
        toDestroy.insert(e.getID());
});

EM.destroyEntities(toDestroy); // destroy after the loop

C++// include/darkmoon/ecs/slotmap.hpp
template <
    typename T,
    std::size_t Capacity = 10,
    typename INDEXT = std::uint32_t
>
struct Slotmap;
T -
Capacity 10
INDEXT uint32_t

textm_indices [ ]  ->  indirection table. Each slot holds:
                    .id  --> position in m_data (when occupied)
                         --> next free slot index (when free, forms the freelist)
                    .gen --> generation counter; incremented on every alloc/free

m_data [ ]     ->  packed array of T. Elements 0..m_size-1 are always valid.
                   Gaps never exist; erase uses swap-and-pop to stay compact.

m_erase [ ]    ->  reverse map: m_erase[dataIdx] = slotIdx that points here.
                   Used to fix up m_indices when swap-and-pop relocates the last element.

C++using key_type = struct { index_type id; gen_type gen; };

// push_back returns a key
auto key = slotmap.push_back(MyComponent{});

// Access via key  asserts in debug if stale
MyComponent& cmp = slotmap[key];

// Safe validity check before access
if (slotmap.is_valid(key)) {
    slotmap[key].value = 42;
}

// Erase increments generation, invalidates all copies of this key
slotmap.erase(key); // returns false if key was already stale

push_back(T&&) key_type O(1)
push_back(const T&) key_type O(1)
operator[](key) T& O(1)
erase(key) bool O(1)
is_valid(key) bool O(1)
clear() void O(N)
begin() / end() T* O(1)
size() std::size_t O(1)
capacity() std::size_t O(1)

textBefore erase(key B):           After erase(key B):

m_data:  [ A | B | C ]         m_data:  [ A | C ]
m_erase: [ a | b | c ]         m_erase: [ a | c ]
                                              ↑ C moved here; m_indices[c].id updated

C++// Compile-time constant wrapper
template <typename T, T VAL>
struct constant { static constexpr T value{ VAL }; };

struct true_type  : constant<bool, true>  {};
struct false_type : constant<bool, false> {};

// Type identity helper used as base for "this struct IS type T"
template <typename T>
struct type_id { using type = T; };

// Type equality
template <typename T, typename U> struct is_same : false_type {};
template <typename T>             struct is_same<T, T> : true_type {};
template <typename T, typename U>
constexpr bool is_same_v{ is_same<T, U>::value };

C++// Base case: N == 0 --> the type is the first in the pack
template <typename T, typename... Ts>
struct nth_type<0, T, Ts...> : type_id<T> {};

// Recursive case: strip the head, decrement N
template <std::size_t N, typename T, typename... Ts>
struct nth_type<N, T, Ts...> : type_id<nth_type_t<N - 1, Ts...>> {};

// Convenience alias
template <std::size_t N, typename... Ts>
using nth_type_t = typename nth_type<N, TypeList<Ts...>>::type;

C++// Match found at head --> position 0
template <typename T, typename... Ts>
struct pos<T, T, Ts...> : constant<std::size_t, 0> {};

// No match yet --> 1 + position in the rest
template <typename T, typename U, typename... Ts>
struct pos<T, U, Ts...> : constant<std::size_t, 1 + pos_v<T, Ts...>> {};

C++template <bool Condition, typename T, typename F> struct IFT          : type_id<F> {};
template <typename T, typename F>                struct IFT<true, T, F> : type_id<T> {};

// Alias for readability
template <bool Condition, typename T, typename F>
using IFT_t = typename IFT<Condition, T, F>::type;

TypeList

C++template <typename... Ts>
struct TypeList {
    // Number of types in the list
    consteval static std::size_t size() noexcept { return sizeof...(Ts); }

    // True if T appears anywhere in the list
    template <typename T>
    consteval static bool contains() noexcept {
        return (false || ... || is_same_v<T, Ts>);
    }

    // Zero-based index of T; static_assert if not found
    template <typename T>
    consteval static std::size_t pos() noexcept {
        static_assert(contains<T>(), "Type not found");
        return pos_v<T, Ts...>;
    }
};

// Usage
using GameCMPs = MP::TypeList<RenderComponent, PhysicsComponent>;
constexpr std::size_t idx = GameCMPs::pos<PhysicsComponent>(); // --> 1
constexpr bool has = GameCMPs::contains<AIComponent>();         // --> false

C++template <typename LIST>
using smallest_type =
    IFT_t<LIST::size() <=  8, std::uint8_t,
    IFT_t<LIST::size() <= 16, std::uint16_t,
    IFT_t<LIST::size() <= 32, std::uint32_t,
    std::uint64_t>>>;

// Example: 3 components + 2 tags = 5 total --> uint8_t (fits in 8 bits)
// Example: 20 total types                  --> uint32_t

C++template <typename TLIST>
struct cmp_tag_traits {
    static_assert(TLIST::size() <= 64, "Component tag list is too large");

    // Narrowest uint that fits one bit per type
    using mask_type = smallest_type<TLIST>;

    // Number of types registered
    consteval static uint8_t size() noexcept { return TLIST::size(); }

    // Bit position of a single component or tag
    template <typename CMPTAG>
    consteval static uint8_t id() noexcept {
        static_assert(TLIST::template contains<CMPTAG>(), "Component or Tag not found");
        return TLIST::template pos<CMPTAG>();
    }

    // OR together the bits for all types in the query
    template <typename... Ts>
    consteval static mask_type mask() noexcept {
        return (0 | ... | (1LL << id<Ts>()));
    }
};

// forEach uses it like this (pseudocode):
using Traits = cmp_tag_traits<MergedList>;
constexpr auto queryMask = Traits::mask<PhysicsComponent, TagPlayer>();
// compiled down to a single integer constant zero runtime cost
if ((entity.mask & queryMask) == queryMask) { /* visit */ }

C++// replace_t: reuse the types in a TypeList with a different variadic template
// TypeList<A,B,C> --> std::tuple<A,B,C>
using SlotmapTuple = replace_t<std::tuple, GameCMPs>;

// forall_insert_template_t: wrap every type in a TypeList with another template
// TypeList<A,B,C> --> TypeList<Slotmap<A>, Slotmap<B>, Slotmap<C>>
using SlotmapList = forall_insert_template_t<Slotmap, GameCMPs>;

C++// Both const and non-const overloads are provided
// Base case (I == tuple size): do nothing
// Recursive case: call f on element I, then advance

MP::for_each_in_tuple(m_slotmaps, [](auto& slotmap) {
    slotmap.clear(); // resets every component storage in one call
});

constant<T, V> struct
true_type / false_type struct
is_same<T,U> trait
nth_type<N, Ts...> trait
pos<T, Ts...> trait
IFT<Cond, T, F> trait
TypeList<Ts...> struct
smallest_type<TL> alias
cmp_tag_traits<TL> struct
replace_t<New, TL> alias
forall_insert_template_t<W, TL> alias
for_each_in_tuple(t, f) function