[M1-ECS-04] Query API + iteration legality - #19
Merged
Merged
Conversation
World::each<T1, T2, ...>(fn, Read/Write tags...) over the archetype SoA rows: - superset match: iterate every entity having all listed components; per-component access declared by the tag type (Read -> const T&, Write -> T&, decided at compile time); tags follow the callable (a pack of parameters must be last to be deducible) - the PRD sketch's argument order is settled in this step - iteration-legality guard (stack-scoped IdSet256 pairs): in-place writes of Read-declared queried components, structural add/remove/destroy/clear touching a matched archetype, and nested each() assert in debug (forked SIGABRT children) and return InvalidArgument + one rate-limited warn + skip-with-log in release (FR-12.3); rejected mutations are never applied, the iteration continues over unmutated storage - no hidden allocations: iteration state is stack-scoped (ids/cols arrays, 256-bit sets); 10k-entity window proves zero process-wide heap allocations + zero reservation delta (test-only operator new counter, non-sanitizer trees; sanitizer trees leak-free) - empty query each<>(fn) visits all live entities ascending slot order; unregistered listed types match nothing (ok, zero visits) - clear() now returns Status (guard check first; dtor never sees an active iteration); existing clear() call sites honor nodiscard New public header laige/sim/query.h (Access/Read/Write tags, IdSet256, full contract); query.cpp defines the guard helpers; entity.h carries each() + the compile-time recursion (packs ride as tuple types - an explicit template argument list cannot partition between consecutive packs; rowRef returns one conditional reference type - a decltype(auto) if/constexpr pair of returns would force an inconsistent deduction). 17 tests in the new ctest query entry (Query*): exact mixed sets, empty-query order + concurrent destroy, unregistered no-match, access-tag reference kinds, pinned visit order, guard release, legal mutation matrix, six forked debug-assert cases, release skip matrix, warn-once rate-limit summary, zero-alloc window (machine-greppable stats lines). laige-api.json regenerated (451 symbols; api-real-tree green). Docs: docs/api/query.md + cross-refs (entity.md, archetype.md, component_registry.md, docs/README, sim README); roadboard/heartbeat updated.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
M1-ECS-04 · Query API + iteration legality
Implements
roadmap/M1-heartbeat.mdM1-ECS-04 (scope only — nodeterministic-iteration contract, no systems):
World::each<T1, T2, ...>(fn, Read/Write tags...)over theM1-ECS-03 SoA rows: superset match (extra components do not exclude
an entity); per-component access declared by tag type (
Read→const T&,Write→T&, decided at compile time; count/typechecked by static_assert). The access tags follow the callable — a
pack of parameters must be last to be deducible, so the PRD sketch
order
(access_flags, fn)is settled here as(fn, tags...)anddocumented in
query.h.each<>(fn)visits all live entities inascending slot order; an unregistered listed type matches nothing
(ok Status, zero visits).
membership-only 256-bit sets: the matched-archetype set, complete
before the first callback, plus the Read-declared component set).
Illegal — an in-place write of a Read-declared queried component, a
structural add/remove/destroy/clear touching a matched source or
target archetype, or a nested
each()— asserts in debug and inrelease returns
ErrorCode::InvalidArgument(no new codes — theregistry stays additive-only) + one rate-limited warn + skip,
never applied, while the iteration continues over the unmutated
storage. Legal:
create()always, structural moves outside thematched set, writes through
Writereferences, in-place overwritesof Write-declared components.
(
ids[N]/cols[N]/matchedIds[256]/two 256-bit sets); the10k-entity × 2-pass window measured zero process-wide heap
allocations (test-only
operator newcounter, non-sanitizertrees; sanitizer trees leak-free) and zero reservation delta
(pool-steady), ≈0.044 µs/visit on the
-O0tree — machine-greppablequery-iteration <stats>lines per ctest run (CORE-001,M1-BENCH-01 baseline input).
clear()is nowStatus(the guard check runs first — a clearunder a live iteration is rejected whole, never partial; the dtor
never sees an active iteration); all call sites honor
[[nodiscard]](internal M1 API change, pre-gate, no externalconsumers).
Tests — new
queryCTest entry (ctest -R query), 17 cases:exact mixed sets (incl. the superset case), empty-query slot order +
a legal concurrent destroy, unregistered no-match, access-tag
reference kinds, pinned archetype-then-slot visit order, guard release
after iteration, the legal-mutation matrix, the release skip matrix
(state unchanged, iteration continues over both entities, the same op
succeeds afterwards), six forked SIGABRT debug-assert children
(one per violation kind), warn-once +
rate_limitedsummary via amemory sink, and the zero-alloc window.
Docs: new
docs/api/query.md(contract + Performance section),cross-refs in
entity.md/archetype.md/component_registry.md/docs index / sim README;
laige-api.jsonregenerated (451 symbols);roadmap checkbox, Progress Board 4/25, one-line Change Log.
Verify: canonical g++ tree zero-warning, full
ctest37/37,ctest -R querygreen (debug: 15 passed + 2 release-only skips;release: 11 passed + 6 debug-only skips); sim suites green on
build-asan(ASan+UBSan, leak-free),build-clang,build-release,build-shared,build-tsan;tools/laige-include-lintOK.