C++ Generational Handles: Add Slot Reuse Only When Needed¶
The main course never reuses IDs. Generations become necessary only when a recycled slot might otherwise let an old reference resolve to a different body.
Name the New Requirement¶
A body occupied slot 1. It is deleted; another body takes slot 1. A reference containing only that slot cannot distinguish them.
A reusable handle needs owner, slot, and generation. Resolution checks all three and live membership. A slot alone remains a storage address, not identity.
Read the Metadata Pool¶
generational_ids.hpp implements a non-copyable, non-movable Pool with slot zero reserved.
Allocation takes a free slot or appends metadata. Release validates the complete handle, marks the slot dead, increments its generation, and links it into a free chain. Release does not allocate.
At the configured generation limit, a slot retires rather than wrapping. Allocation then uses another slot. A finite handle space cannot guarantee unlimited reuse without an exhaustion policy.
The default limit is the largest uint64_t value. Tests use the same templated implementation with limits 1 and 2 to exercise boundaries without billions of operations.
The Pool Is Not a World¶
It stores no positions, velocities, or lifetime. A caller must remove all component data before releasing a slot. Otherwise a newly allocated identity can inherit stale component data.
In a complete world, construction failures would also need an allocation rollback policy. This isolated experiment does not provide transactional entity construction or a production free-list allocator.
Run the Example¶
Use the optional build. Its first line is:
The generation tests verify owner collisions, empty handles, stale copies, repeated release, reuse, retirement, storage growth, and a handle surviving its former Pool's lifetime without resolving in another Pool.
Invariants to Review¶
| Rule | Failure it prevents |
|---|---|
| Owner must match | A slot from another world resolves |
| Slot must be in range and live | Empty/deleted handle resolves |
| Generation must match | Old handle resolves after reuse |
| Limit retires rather than wraps | Ancient handle becomes valid again |
| Data cleaned before release | New identity inherits old components |
No claim follows that this is cheaper than non-reused IDs. It solves a different requirement and adds policies to maintain.
Exercise¶
With limit 2, allocate, release, allocate, release, allocate. Predict slots and generations: (1,1), (1,2), then (2,1). Explain why retirement is intentional.
Extension overview · Next: Component storage · Main-course identity