# A C++ data-oriented registry with compile-time type mapping and cache-efficient handle-based storage.

> Source: <https://gist.github.com/unrays/158d58e795bf9d0606ec344fd57fcdb8>
> Published: 2026-05-22 17:29:41+00:00

Created
May 22, 2026 17:29

-
-
Save unrays/158d58e795bf9d0606ec344fd57fcdb8 to your computer and use it in GitHub Desktop.

A C++ data-oriented registry with compile-time type mapping and cache-efficient handle-based storage.

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](https://github.co/hiddenchars)| // Copyright (c) May 2026 Félix-Olivier Dumas. All rights reserved. | |
| // Licensed under the terms described in the LICENSE file | |
| #pragma once | |
| #include <cstddef> | |
| #include <tuple> | |
| #include <utility> | |
| #include <type_traits> | |
| #include <iostream> | |
| /***************************************************************************/ | |
| /* SENTINEL */ | |
| /***************************************************************************/ | |
| struct SENTINEL {}; | |
| /***************************************************************************/ | |
| /* LINEAR TABLE */ | |
| /***************************************************************************/ | |
| template<typename... Types> | |
| struct LinearTable : Types... {}; | |
| template<typename Key, typename Value> | |
| struct Entry {}; | |
| /***************************************************************************/ | |
| /* ENTRY TRAITS */ | |
| /***************************************************************************/ | |
| template<typename...> | |
| struct entry_traits; | |
| template<typename K, typename V> | |
| struct entry_traits<Entry<K, V>> | |
| { | |
| using key = K; | |
| using value = V; | |
| }; | |
| /***************************************************************************/ | |
| /* LOOKUP */ | |
| /***************************************************************************/ | |
| template<typename...> | |
| struct lookup; | |
| template<typename Key, typename Target, typename... Rest> | |
| struct lookup<Key, LinearTable<Entry<Key, Target>, Rest...>> | |
| { | |
| using type = Target; | |
| }; | |
| template<typename Key, typename Head, typename... Rest> | |
| struct lookup<Key, LinearTable<Head, Rest...>> | |
| { | |
| using type = typename lookup<Key, Rest...>::type; | |
| }; | |
| template<typename Key, typename Empty> | |
| struct lookup<Key, Empty> | |
| { | |
| using type = SENTINEL; | |
| }; | |
| template<typename Key, typename Table> | |
| using table_lookup_t = typename lookup<Key, Table>::type; | |
| /***************************************************************************/ | |
| /* SIZE OF TABLE */ | |
| /***************************************************************************/ | |
| template<typename> | |
| struct size_of; | |
| template<typename... Types> | |
| struct size_of<LinearTable<Types...>> | |
| { | |
| static constexpr std::size_t value = sizeof...(Types); | |
| }; | |
| template<typename Table> | |
| static constexpr std::size_t size_of_v = size_of<Table>::value; | |
| /***************************************************************************/ | |
| /* HANDLE PROVIDERS */ | |
| /***************************************************************************/ | |
| struct DefaultHandleProvider | |
| { | |
| template<typename Tp> | |
| [[nodiscard]] constexpr std::size_t operator()(const Tp* obj) | |
| { | |
| return obj->id; | |
| } | |
| }; | |
| /***************************************************************************/ | |
| /* REGISTRY STORAGE BUILDER */ | |
| /***************************************************************************/ | |
| template<typename Table, template<typename> typename StorageType> | |
| struct make_registry_storage; | |
| template<typename... Entries, template<typename...> typename StorageType> | |
| struct make_registry_storage<LinearTable<Entries...>, StorageType> | |
| { | |
| using type = std::tuple< | |
| StorageType<typename entry_traits<Entries>::value>... | |
| >; | |
| }; | |
| template<typename Table, template<typename...> typename StorageType> | |
| using make_registry_storage_t = | |
| typename make_registry_storage<Table, StorageType>::type; | |
| /***************************************************************************/ | |
| /* SMART STORAGE */ | |
| /***************************************************************************/ | |
| template<typename Tp, std::size_t N> | |
| class SmartStorage final | |
| { | |
| protected: | |
| static constexpr std::uint16_t MaxStack = 1 << 14; | |
| using OnlyIfStackEligible = | |
| std::conditional_t< | |
| (N * sizeof(Tp) <= MaxStack), | |
| std::byte[N * sizeof(Tp)], | |
| std::monostate | |
| >; | |
| public: | |
| SmartStorage() | |
| : is_constructed_{} | |
| { | |
| if constexpr (is_heap_eligible_) { | |
| buffer_ptr_ = static_cast<std::byte*>( | |
| ::operator new(N * sizeof(Tp), std::align_val_t(alignof(Tp))) | |
| ); | |
| } | |
| else { | |
| buffer_ptr_ = &stack_buffer_[0]; | |
| } | |
| } | |
| ~SmartStorage() noexcept | |
| { | |
| reset(); | |
| if constexpr (is_heap_eligible_) { | |
| ::operator delete(buffer_ptr_, std::align_val_t(alignof(Tp))); | |
| } | |
| } | |
| protected: | |
| void throw_if_out_of_range(std::size_t index) const | |
| { | |
| if (index >= N) [[unlikely]] { | |
| throw std::out_of_range( | |
| "[EXOTIC::SmartStorage] index out of range: " | |
| + std::to_string(index) | |
| + " (valid range: 0.." | |
| + std::to_string(N - 1) | |
| + ")" | |
| ); | |
| } | |
| } | |
| void throw_if_existing(std::size_t index) const | |
| { | |
| if (is_constructed_[index]) [[unlikely]] { | |
| throw std::runtime_error( | |
| "[EXOTIC::SmartStorage] construction conflict: slot already occupied at index " | |
| + std::to_string(index) | |
| ); | |
| } | |
| } | |
| void throw_if_nonexistent(std::size_t index) const | |
| { | |
| if (!is_constructed_[index]) [[unlikely]] { | |
| throw std::runtime_error( | |
| "[EXOTIC::SmartStorage] access violation: no object constructed at index " | |
| + std::to_string(index) | |
| ); | |
| } | |
| } | |
| public: | |
| [[nodiscard]] Tp& get(std::size_t index) | |
| { | |
| throw_if_out_of_range(index); | |
| throw_if_nonexistent(index); | |
| return *reinterpret_cast<Tp*>(buffer_ptr_ + index * sizeof(Tp)); | |
| } | |
| [[nodiscard]] const Tp& get(std::size_t index) const | |
| { | |
| throw_if_out_of_range(index); | |
| throw_if_nonexistent(index); | |
| return *reinterpret_cast<const Tp*>(buffer_ptr_ + index * sizeof(Tp)); | |
| } | |
| public: | |
| template<typename... Types> | |
| Tp& emplace(std::size_t index, Types&&... args) | |
| noexcept(std::is_nothrow_constructible_v<std::decay<Tp>, Types&&...>) | |
| { | |
| throw_if_out_of_range(index); | |
| throw_if_existing(index); | |
| Tp* ptr = reinterpret_cast<Tp*>(buffer_ptr_ + index * sizeof(Tp)); | |
| new (ptr) Tp(std::forward<Types>(args)...); | |
| is_constructed_[index] = true; | |
| return *ptr; | |
| } | |
| public: | |
| Tp& insert(std::size_t index, Tp&& obj) | |
| { | |
| throw_if_out_of_range(index); | |
| Tp* ptr = reinterpret_cast<Tp*>(buffer_ptr_ + index * sizeof(Tp)); | |
| if (is_constructed_[index]) { | |
| *ptr = obj; | |
| } | |
| else { | |
| new (ptr) Tp(obj); | |
| is_constructed_[index] = true; | |
| } | |
| return *ptr; | |
| } | |
| Tp& insert(std::size_t index, Tp& obj) | |
| { | |
| throw_if_out_of_range(index); | |
| Tp* ptr = reinterpret_cast<Tp*>(buffer_ptr_ + index * sizeof(Tp)); | |
| if (is_constructed_[index]) { | |
| *ptr = obj; | |
| } | |
| else { | |
| new (ptr) Tp(obj); | |
| is_constructed_[index] = true; | |
| } | |
| return *ptr; | |
| } | |
| public: | |
| void destroy(std::size_t index) noexcept | |
| { | |
| throw_if_out_of_range(index); | |
| throw_if_nonexistent(index); | |
| Tp* ptr = reinterpret_cast<Tp*>(buffer_ptr_ + index * sizeof(Tp)); | |
| ptr->~Tp(); | |
| is_constructed_[index] = false; | |
| } | |
| void reset() noexcept | |
| { | |
| for (std::size_t i = 0; i < N; ++i) { | |
| if (is_constructed_[i]) { | |
| Tp* ptr = reinterpret_cast<Tp*>(buffer_ptr_ + i * sizeof(Tp)); | |
| ptr->~Tp(); | |
| is_constructed_[i] = false; | |
| } | |
| } | |
| std::memset(buffer_ptr_, 0, N * sizeof(Tp)); | |
| } | |
| public: | |
| [[nodiscard]] constexpr std::size_t capacity() const noexcept | |
| { | |
| return N; | |
| } | |
| private: | |
| alignas(Tp) OnlyIfStackEligible stack_buffer_; | |
| std::byte* buffer_ptr_; | |
| static constexpr bool is_heap_eligible_ = | |
| std::is_same_v<OnlyIfStackEligible, std::monostate>; | |
| std::array<bool, N> is_constructed_; | |
| }; | |
| /***************************************************************************/ | |
| /* MULTI STORAGE REGISTRY */ | |
| /***************************************************************************/ | |
| template<typename Table, typename HandleProvider, std::size_t N = 1 << 14> | |
| class MultiStorageRegistry final | |
| { | |
| protected: | |
| static constexpr std::size_t BytesPerStorage = N / size_of_v<Table>; | |
| template<typename Up> | |
| using RegistryStorageStrategy = | |
| SmartStorage<Up, BytesPerStorage / sizeof(Up)>; | |
| public: | |
| explicit MultiStorageRegistry(HandleProvider provider = {}) | |
| : handleProvider_(provider), storage_{} | |
| { | |
| std::cout << "[NODE REGISTRY CTOR] this = " << this << "\n"; | |
| } | |
| ~MultiStorageRegistry() noexcept | |
| { | |
| reset(); | |
| } | |
| public: | |
| template<typename Tp> | |
| auto& resolve_storage() noexcept | |
| { | |
| using Result = std::decay_t< | |
| table_lookup_t<std::decay_t<Tp>, Table> | |
| >; | |
| static_assert( | |
| !std::is_same_v<Result, PRYSMA_SENTINEL>, | |
| "Unable to resolve the requested type from the LinearTable." | |
| ); | |
| return std::get<RegistryStorageStrategy<Result>>(storage_); | |
| } | |
| public: | |
| template<typename Tp> | |
| [[nodiscard]] const auto& get(const Tp* obj) const noexcept | |
| { | |
| const auto& storage = resolve_storage<Tp>(); | |
| return storage.get(handleProvider_(obj)); | |
| } | |
| template<typename Up, typename Tp> | |
| [[nodiscard]] const auto& get_for(const Tp* obj) const noexcept | |
| { | |
| const auto& storage = std::get<RegistryStorageStrategy<Up>>(storage_); | |
| return storage.get(handleProvider_(obj)); | |
| } | |
| public: | |
| template<typename Tp> | |
| [[nodiscard]] auto& get(const Tp* obj) noexcept | |
| { | |
| auto& storage = resolve_storage<Tp>(); | |
| return storage.get(handleProvider_(obj)); | |
| } | |
| template<typename Up, typename Tp> | |
| [[nodiscard]] auto& get_for(const Tp* obj) noexcept | |
| { | |
| auto& storage = std::get<RegistryStorageStrategy<Up>>(storage_); | |
| return storage.get(handleProvider_(obj)); | |
| } | |
| public: | |
| template<typename Tp, typename... Types> | |
| auto& construct(const Tp* obj, Types&&... args) | |
| { | |
| auto& storage = resolve_storage<Tp>(); | |
| return storage.emplace(handleProvider_(obj), std::forward<Types>(args)...); | |
| } | |
| template<typename Up, typename Tp, typename... Types> | |
| auto& construct_for(const Tp* obj, Types&&... args) | |
| { | |
| auto& storage = std::get<RegistryStorageStrategy<Up>>(storage_); | |
| return storage.emplace(handleProvider_(obj), std::forward<Types>(args)...); | |
| } | |
| public: | |
| template<typename Tp, typename Up> | |
| auto& assign(const Tp* obj, Up&& arg) | |
| { | |
| auto& storage = resolve_storage<Tp>(); | |
| return storage.insert(handleProvider_(obj), std::forward<Up>(arg)); | |
| } | |
| template<typename Up, typename Tp> | |
| auto& assign_for(const Tp* obj, Up&& arg) | |
| { | |
| auto& storage = std::get<RegistryStorageStrategy<Up>>(storage_); | |
| return storage.insert(handleProvider_(obj), std::forward<Up>(arg)); | |
| } | |
| public: | |
| void reset() noexcept | |
| { | |
| std::apply([](auto&... storage) { | |
| (storage.reset(), ...); | |
| }, storage_); | |
| } | |
| template<typename Up> | |
| void reset_for() noexcept | |
| { | |
| auto& storage = std::get<RegistryStorageStrategy<Up>>(storage_); | |
| storage.reset(); | |
| } | |
| private: | |
| make_registry_storage_t<Table, RegistryStorageStrategy> storage_; | |
| HandleProvider handleProvider_; | |
| }; | |
| using ExampleRegistry = MultiStorageRegistry<ExampleRegistryTable, DefaultHandleProvider, 1 << 10>; |
