R-Type  2
Doom but in better
Loading...
Searching...
No Matches
ComponentContainer.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <vector>
4#include <optional>
5#include <variant>
6#include <memory>
7#include <cassert>
8#include <algorithm>
9
10#include "DenseArray.hpp"
11#include "SparseArray.hpp"
12
23template <typename Component, typename Allocator = std::allocator<Component>>
25public:
27 using value_type = Component;
28 using optional_t = std::optional<Component>;
34 using const_reference = const optional_t&;
35 using iterator = typename std::vector<optional_t, Allocator>::iterator;
36 using const_iterator = typename std::vector<optional_t, Allocator>::const_iterator;
42
49 reference operator[](size_t idx) {
50 return std::visit([&](auto& storage) -> reference { return storage[idx]; }, _storage);
51 }
52
59 const_reference operator[](size_t idx) const {
60 return std::visit([&](auto const& storage) -> const_reference { return storage[idx]; }, _storage);
61 }
62
69 if (std::holds_alternative<sparse_storage_t>(_storage)) {
70 auto& sparse = std::get<sparse_storage_t>(_storage);
71 return sparse.begin();
72 } else {
73 auto& dense = std::get<dense_storage_t>(_storage);
74 return dense.begin();
75 }
76 }
77
84 if (std::holds_alternative<sparse_storage_t>(_storage)) {
85 auto& sparse = std::get<sparse_storage_t>(_storage);
86 return sparse.begin();
87 } else {
88 auto& dense = std::get<dense_storage_t>(_storage);
89 return dense.begin();
90 }
91 }
92
99 if (std::holds_alternative<sparse_storage_t>(_storage)) {
100 auto& sparse = std::get<sparse_storage_t>(_storage);
101 return sparse.cbegin();
102 } else {
103 auto& dense = std::get<dense_storage_t>(_storage);
104 return dense.cbegin();
105 }
106 }
107
114 if (std::holds_alternative<sparse_storage_t>(_storage)) {
115 auto& sparse = std::get<sparse_storage_t>(_storage);
116 return sparse.end();
117 } else {
118 auto& dense = std::get<dense_storage_t>(_storage);
119 return dense.end();
120 }
121 }
122
129 if (std::holds_alternative<sparse_storage_t>(_storage)) {
130 auto& sparse = std::get<sparse_storage_t>(_storage);
131 return sparse.end();
132 } else {
133 auto& dense = std::get<dense_storage_t>(_storage);
134 return dense.end();
135 }
136 }
137
144 if (std::holds_alternative<sparse_storage_t>(_storage)) {
145 auto& sparse = std::get<sparse_storage_t>(_storage);
146 return sparse.cend();
147 } else {
148 auto& dense = std::get<dense_storage_t>(_storage);
149 return dense.cend();
150 }
151 }
152
158 size_type size() const {
159 if (std::holds_alternative<sparse_storage_t>(_storage)) {
160 auto& sparse = std::get<sparse_storage_t>(_storage);
161 return sparse.size();
162 } else {
163 auto& dense = std::get<dense_storage_t>(_storage);
164 return dense.size();
165 }
166 }
167
174 void insert_at(size_type id, const Component& component) {
175 if (std::holds_alternative<sparse_storage_t>(_storage)) {
176 auto& sparse = std::get<sparse_storage_t>(_storage);
177 sparse.insert_at(id, component);
178 } else {
179 auto& dense = std::get<dense_storage_t>(_storage);
180 dense.insert_at(id, component);
181 }
182 }
183
190 void insert_at(size_type id, Component&& component) {
191 if (std::holds_alternative<sparse_storage_t>(_storage)) {
192 auto& sparse = std::get<sparse_storage_t>(_storage);
193 sparse.insert_at(id, std::forward<Component>(component));
194 } else {
195 auto& dense = std::get<dense_storage_t>(_storage);
196 dense.insert_at(id, std::forward<Component>(component));
197 }
198 }
199
207 template <typename... Params>
208 void emplace_at(size_type id, Params&&... params) {
209 if (std::holds_alternative<sparse_storage_t>(_storage)) {
210 auto& sparse = std::get<sparse_storage_t>(_storage);
211 if (id >= sparse.size()) {
212 sparse.resize(id + 1);
213 }
214
215 sparse[id].emplace(std::forward<Params>(params)...);
216 } else {
217 auto& dense = std::get<dense_storage_t>(_storage);
218
219 dense.emplace_at(id, std::forward<Params>(params)...);
220 }
221 }
222
229 std::optional<Component> get(const value_type& id) const {
230 if (std::holds_alternative<sparse_storage_t>(_storage)) {
231 const auto& sparse = std::get<sparse_storage_t>(_storage);
232 if (id < sparse.size()) {
233 return sparse[id];
234 }
235 } else {
236 const auto& dense = std::get<dense_storage_t>(_storage);
237 if (id < dense.size()) {
238 return dense[id];
239 }
240 }
241 return std::nullopt;
242 }
243
249 void erase(size_type id) {
250 if (std::holds_alternative<sparse_storage_t>(_storage)) {
251 auto& sparse = std::get<sparse_storage_t>(_storage);
252 if (id < sparse.size()) {
253 sparse[id].reset();
254 }
255 } else {
256 auto& dense = std::get<dense_storage_t>(_storage);
257 if (id < dense.size()) {
258 dense.erase(id);
259 }
260 }
261 }
262
268 void resize(size_type new_size) {
269 if (std::holds_alternative<sparse_storage_t>(_storage)) {
270 auto& sparse = std::get<sparse_storage_t>(_storage);
271 sparse.resize(new_size);
272 }
273 }
274
281 void optimize_storage(size_type sparse_threshold, size_type dense_threshold) {
282 if (std::holds_alternative<sparse_storage_t>(_storage)) {
283 auto& sparse = std::get<sparse_storage_t>(_storage);
284 size_type filled = std::count_if(sparse.begin(), sparse.end(), [](const auto& opt) { return opt.has_value(); });
285 if (filled >= dense_threshold) {
286 dense_storage_t dense;
287 migrate_storage(dense);
288 _storage = std::move(dense);
289 }
290 } else {
291 auto& dense = std::get<dense_storage_t>(_storage);
292 if (dense.size() <= sparse_threshold) {
293 sparse_storage_t sparse;
294 migrate_storage(sparse);
295 _storage = std::move(sparse);
296 }
297 }
298 }
299
300private:
304 using ComponentStorage = std::variant<sparse_storage_t, dense_storage_t>;
305 ComponentStorage _storage;
313 template <typename NewContainer>
314 void migrate_storage(NewContainer& new_storage) {
315 if (std::holds_alternative<sparse_storage_t>(_storage)) {
316 const auto& sparse = std::get<sparse_storage_t>(_storage);
317 for (size_type i = 0; i < sparse.size(); ++i) {
318 if (sparse[i]) {
319 new_storage.insert_at(i, *sparse[i]);
320 }
321 }
322 } else {
323 const auto& dense = std::get<dense_storage_t>(_storage);
324 size_type max_id = *std::max_element(dense.begin(), dense.end());
325 new_storage.resize(max_id + 1);
326 for (size_type i = 0; i < dense.size(); ++i) {
327 new_storage[i] = dense[i];
328 }
329 }
330 }
331};
Manages a collection of components associated with entities in an ECS (Entity-Component-System) archi...
Component value_type
Type aliases for convenience.
const_iterator end() const
Returns a const iterator to the end of the component container.
iterator end()
Returns an iterator to the end of the component container.
typename std::vector< optional_t, Allocator >::iterator iterator
std::optional< Component > get(const value_type &id) const
Retrieves a component by its id.
reference operator[](size_t idx)
Accesses the component at a given index.
void emplace_at(size_type id, Params &&... params)
Constructs and inserts a component at a specific ID.
size_type size() const
Returns the number of components stored in the array.
void insert_at(size_type id, const Component &component)
Inserts a component at a specific ID.
typename std::vector< optional_t, Allocator >::const_iterator const_iterator
const_iterator cbegin() const
Returns a const iterator to the beginning of the component container.
ComponentContainer()
Default constructor.
const_reference operator[](size_t idx) const
Accesses the component at a given index (const version).
const optional_t & const_reference
typename sparse_storage_t::size_type size_type
const_iterator cend() const
Returns a const iterator to the end of the component container.
iterator begin()
Returns an iterator to the beginning of the component container.
void optimize_storage(size_type sparse_threshold, size_type dense_threshold)
Optimizes the storage type based on thresholds for sparse and dense storage.
std::optional< Component > optional_t
void resize(size_type new_size)
Resizes the container to a new size.
const_iterator begin() const
Returns a const iterator to the beginning of the component container.
void erase(size_type id)
Removes a component at a specific ID.
void insert_at(size_type id, Component &&component)
Inserts a component at a specific ID (move version).
A container that provides dense storage for components with a mapping between entity IDs and componen...
A container that provides sparse storage for optional components.
typename container_t::size_type size_type