sandbox
Loading...
Searching...
No Matches
view.hpp
1// SPDX-License-Identifier: MIT
2#ifndef LIBSBX_VIEW_HPP_
3#define LIBSBX_VIEW_HPP_
4
5#include <algorithm>
6#include <tuple>
7#include <ranges>
8#include <vector>
9#include <array>
10
11#include <libsbx/utility/type_list.hpp>
12
13#include <libsbx/memory/concepts.hpp>
14#include <libsbx/memory/iterable_adaptor.hpp>
15
16#include <libsbx/ecs/detail/view_iterator.hpp>
17
18namespace sbx::ecs {
19
20namespace detail {
21
22template<typename Type, bool IsChecked, std::size_t Get, std::size_t Exclude>
23requires (std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>)
25
26 template<typename Return, typename View, typename Other, std::size_t... GetLhs, std::size_t... GetRhs>
27 friend auto view_pack(const View&, const Other&, std::index_sequence<GetLhs...>, std::index_sequence<GetRhs...>) -> Return;
28
29public:
30
31 using common_type = Type;
32 using entity_type = typename Type::entity_type;
33 using size_type = std::size_t;
34 using difference_type = std::ptrdiff_t;
36
37 auto refresh() noexcept -> void {
38 auto position = static_cast<size_type>(_index != Get) * Get;
39
40 for (; position < Get && _pools[position] != nullptr; ++position) { }
41
42 if (position == Get) {
43 _unchecked_refresh();
44 }
45 }
46
47 [[nodiscard]] auto handle() const noexcept -> const common_type* {
48 return (_index != Get) ? _pools[_index] : nullptr;
49 }
50
51 [[nodiscard]] auto size_hint() const noexcept -> size_type {
52 return (_index != Get) ? _offset() : size_type{};
53 }
54
55 [[nodiscard]] auto begin() const noexcept -> iterator {
56 return (_index != Get) ? iterator{_pools[_index]->end() - static_cast<difference_type>(_offset()), _pools, _filter, _index} : iterator{};
57 }
58
59 [[nodiscard]] auto end() const noexcept -> iterator {
60 return (_index != Get) ? iterator{_pools[_index]->end(), _pools, _filter, _index} : iterator{};
61 }
62
63 [[nodiscard]] auto front() const noexcept -> entity_type {
64 const auto it = begin();
65 return it != end() ? *it : null_entity;
66 }
67
68 [[nodiscard]] auto back() const noexcept -> entity_type {
69 if(_index != Get) {
70 auto it = _pools[_index]->rbegin();
71 const auto last = it + static_cast<difference_type>(_offset());
72
73 for (const auto idx = static_cast<difference_type>(_index); it != last && !(detail::all_of(_pools.begin(), _pools.begin() + idx, *it) && detail::all_of(_pools.begin() + idx + 1, _pools.end(), *it)); ++it) { }
74
75 return it == last ? null_entity : *it;
76 }
77
78 return null_entity;
79 }
80
81 [[nodiscard]] auto find(const entity_type entity) const noexcept -> iterator {
82 return contains(entity) ? iterator{_pools[_index]->find(entity), _pools, _index} : end();
83 }
84
85 [[nodiscard]] explicit operator bool() const noexcept {
86 return (_index != Get); // && detail::is_fully_initialized(filter.begin(), filter.end(), placeholder);
87 }
88
89 [[nodiscard]] auto contains(const entity_type entity) const noexcept -> bool {
90 return (_index != Get) && detail::all_of(_pools.begin(), _pools.end(), entity) && _pools[_index]->_index(entity) < _offset();
91 }
92
93protected:
94
95 basic_common_view() noexcept
96 : _pools{},
97 _index{Get} { }
98
99 basic_common_view(std::array<const common_type*, Get> pools, std::array<const common_type*, Exclude> filter) noexcept
100 : _pools{pools},
101 _filter{filter},
102 _index{Get} {
103 _unchecked_refresh();
104 }
105
106 [[nodiscard]] auto pool_at(const size_type position) const noexcept -> const common_type* {
107 return _pools[position];
108 }
109
110 auto set_pool_at(const size_type position, const common_type* element) noexcept -> void {
111 utility::assert_that(element != nullptr, "Unexpected element");
112 _pools[position] = element;
113 refresh();
114 }
115
116 auto use(const size_type position) noexcept -> void {
117 _index = (_index != Get) ? position : Get;
118 }
119
120private:
121
122 [[nodiscard]] auto _offset() const noexcept {
123 utility::assert_that(_index != Get, "Invalid view");
124 return (_pools[_index]->policy() == deletion_policy::swap_only) ? _pools[_index]->free_list() : _pools[_index]->size();
125 }
126
127 auto _unchecked_refresh() noexcept -> void {
128 _index = 0u;
129
130 if constexpr (Get > 1u) {
131 for (auto position = 1u; position < Get; ++position) {
132 if (_pools[position]->size() < _pools[_index]->size()) {
133 _index = position;
134 }
135 }
136 }
137 }
138
139 std::array<const common_type*, Get> _pools;
140 std::array<const common_type*, Exclude> _filter;
141 size_type _index;
142
143}; // class basic_common_view
144
145template<typename Type, deletion_policy Policy>
146requires (std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>)
148
149public:
150
151 using common_type = Type;
152 using entity_type = typename common_type::entity_type;
153 using size_type = std::size_t;
154 using difference_type = std::ptrdiff_t;
155 using iterator = std::conditional_t<Policy == deletion_policy::in_place, detail::view_iterator<common_type, true, 1u, 0u>, typename common_type::iterator>;
156 using reverse_iterator = std::conditional_t<Policy == deletion_policy::in_place, void, typename common_type::reverse_iterator>;
157
158 [[nodiscard]] auto handle() const noexcept -> const common_type* {
159 return _leading;
160 }
161
162 template<typename..., deletion_policy Pol = Policy>
163 requires (Pol != deletion_policy::in_place)
164 [[nodiscard]] auto size() const noexcept -> size_type {
165 if constexpr (Policy == deletion_policy::swap_and_pop) {
166 return _leading ? _leading->size() : size_type{};
167 } else {
168 static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy");
169 return _leading ? _leading->free_list() : size_type{};
170 }
171 }
172
173 template<typename..., deletion_policy Pol = Policy>
174 requires (Pol == deletion_policy::in_place)
175 [[nodiscard]] auto size_hint() const noexcept -> size_type {
176 return _leading ? _leading->size() : size_type{};
177 }
178
179 template<typename..., deletion_policy Pol = Policy>
180 requires (Pol != deletion_policy::in_place)
181 [[nodiscard]] auto is_empty() const noexcept -> bool {
182 if constexpr (Policy == deletion_policy::swap_and_pop) {
183 return !_leading || _leading->is_empty();
184 } else {
185 static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy");
186 return !_leading || (_leading->free_list() == 0u);
187 }
188 }
189
190 [[nodiscard]] auto begin() const noexcept -> iterator {
191 if constexpr (Policy == deletion_policy::swap_and_pop) {
192 return _leading ? _leading->begin() : iterator{};
193 } else if constexpr (Policy == deletion_policy::swap_only) {
194 return _leading ? (_leading->end() - static_cast<difference_type>(_leading->free_list())) : iterator{};
195 } else {
196 static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy");
197 return _leading ? iterator{_leading->begin(), {_leading}, {}, 0u} : iterator{};
198 }
199 }
200
201 [[nodiscard]] auto end() const noexcept -> iterator {
202 if constexpr (Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::swap_only) {
203 return _leading ? _leading->end() : iterator{};
204 } else {
205 static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy");
206 return _leading ? iterator{_leading->end(), {_leading}, {}, 0u} : iterator{};
207 }
208 }
209
210 template<typename..., deletion_policy Pol = Policy>
211 requires (Pol != deletion_policy::in_place)
212 [[nodiscard]] auto rbegin() const noexcept -> reverse_iterator {
213 return _leading ? _leading->rbegin() : reverse_iterator{};
214 }
215
216 template<typename..., deletion_policy Pol = Policy>
217 requires (Pol != deletion_policy::in_place)
218 [[nodiscard]] auto rend() const noexcept -> reverse_iterator {
219 if constexpr (Policy == deletion_policy::swap_and_pop) {
220 return _leading ? _leading->rend() : reverse_iterator{};
221 } else {
222 static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy");
223 return _leading ? (_leading->rbegin() + static_cast<difference_type>(_leading->free_list())) : reverse_iterator{};
224 }
225 }
226
227 [[nodiscard]] auto front() const noexcept -> entity_type {
228 if constexpr (Policy == deletion_policy::swap_and_pop) {
229 return is_empty() ? null_entity : *_leading->begin();
230 } else if constexpr (Policy == deletion_policy::swap_only) {
231 return is_empty() ? null_entity : *(_leading->end() - static_cast<difference_type>(_leading->free_list()));
232 } else {
233 static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy");
234 const auto it = begin();
235
236 return (it == end()) ? null_entity : *it;
237 }
238 }
239
240 [[nodiscard]] auto back() const noexcept -> entity_type {
241 if constexpr (Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::swap_only) {
242 return is_empty() ? null_entity : *_leading->rbegin();
243 } else {
244 static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy");
245
246 if (_leading) {
247 auto it = _leading->rbegin();
248 const auto last = _leading->rend();
249
250 for(; (it != last) && (*it == tombstone_entity); ++it) { }
251
252 return it == last ? null_entity : *it;
253 }
254
255 return null_entity;
256 }
257 }
258
259 [[nodiscard]] auto find(const entity_type entity) const noexcept -> iterator {
260 if constexpr (Policy == deletion_policy::swap_and_pop) {
261 return _leading ? _leading->find(entity) : iterator{};
262 } else if constexpr (Policy == deletion_policy::swap_only) {
263 const auto it = _leading ? _leading->find(entity) : iterator{};
264 return _leading && (static_cast<size_type>(it.index()) < _leading->free_list()) ? it : iterator{};
265 } else {
266 return _leading ? iterator{_leading->find(entity), {_leading}, {}, 0u} : iterator{};
267 }
268 }
269
270 [[nodiscard]] explicit operator bool() const noexcept {
271 return (_leading != nullptr);
272 }
273
274 [[nodiscard]] auto contains(const entity_type entity) const noexcept -> bool {
275 if constexpr (Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::in_place) {
276 return _leading && _leading->contains(entity);
277 } else {
278 static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy");
279 return _leading && _leading->contains(entity) && (_leading->index(entity) < _leading->free_list());
280 }
281 }
282
283protected:
284
285 basic_storage_view() noexcept = default;
286
287 basic_storage_view(const common_type* value) noexcept
288 : _leading{value} {
289 utility::assert_that(_leading->policy() == Policy, "Unexpected storage policy");
290 }
291
292private:
293
294 const common_type* _leading;
295
296}; // class basic_storage_view
297
298
299} // namespace detail
300
301template<typename... Type>
302struct get_t final : utility::type_list<Type...> {
303 explicit constexpr get_t() = default;
304}; // struct get_t
305
306template<typename... Type>
307inline constexpr get_t<Type...> get{};
308
309template<typename... Type>
310struct exclude_t final : utility::type_list<Type...> {
311 explicit constexpr exclude_t() = default;
312}; // struct exclude
313
314template<typename... Type>
315inline constexpr exclude_t<Type...> exclude{};
316
317template<typename, typename>
319
320template<typename... Get, typename... Exclude>
321requires (sizeof...(Get) != 0u)
322class basic_view<get_t<Get...>, exclude_t<Exclude...>> : public detail::basic_common_view<std::common_type_t<typename Get::base_type...>, detail::tombstone_check_v<Get...>, sizeof...(Get), sizeof...(Exclude)> {
323
324 using base_type = detail::basic_common_view<std::common_type_t<typename Get::base_type...>, detail::tombstone_check_v<Get...>, sizeof...(Get), sizeof...(Exclude)>;
325
326 template<std::size_t Index>
327 using element_at = utility::type_list_element_t<Index, utility::type_list<Get..., Exclude...>>;
328
329 template<typename Type>
330 inline static constexpr auto index_of = utility::type_list_index_v<std::remove_const_t<Type>, utility::type_list<typename Get::element_type..., typename Exclude::element_type...>>;
331
332public:
333
334 using common_type = typename base_type::common_type;
335 using entity_type = typename base_type::entity_type;
336 using size_type = typename base_type::size_type;
337 using difference_type = std::ptrdiff_t;
338 using iterator = typename base_type::iterator;
340
341 basic_view() noexcept
342 : base_type{} { }
343
344
345 basic_view(Get&... pools, Exclude&... filter) noexcept
346 : base_type{{&pools...}, {&filter...}} { }
347
348 basic_view(std::tuple<Get&...> pools, std::tuple<Exclude&...> filter = {}) noexcept
349 : basic_view{std::make_from_tuple<basic_view>(std::tuple_cat(pools, filter))} { }
350
351 template<typename Type>
352 [[nodiscard]] auto* storage() const noexcept {
353 return storage<index_of<Type>>();
354 }
355
356 template<std::size_t Index>
357 [[nodiscard]] auto* storage() const noexcept {
358 return static_cast<element_at<Index>*>(const_cast<utility::constness_as_t<common_type, element_at<Index>>*>(base_type::pool_at(Index)));
359 }
360
361 template<typename Type>
362 auto set_storage(Type& element) noexcept -> void {
363 set_storage<index_of<typename Type::element_type>>(element);
364 }
365
366 template<std::size_t Index, typename Type>
367 requires (std::is_convertible_v<Type&, element_at<Index>&>)
368 void set_storage(Type& element) noexcept {
369 base_type::set_pool_at(Index, &element);
370 }
371
372 template<typename Type, typename... Other>
373 [[nodiscard]] auto get(const entity_type entity) const -> decltype(auto) {
374 return get<index_of<Type>, index_of<Other>...>(entity);
375 }
376
377 template<std::size_t... Index>
378 [[nodiscard]] auto get(const entity_type entity) const -> decltype(auto) {
379 if constexpr(sizeof...(Index) == 0) {
380 return _get(entity, std::index_sequence_for<Get...>{});
381 } else if constexpr(sizeof...(Index) == 1) {
382 return (storage<Index>()->get(entity), ...);
383 } else {
384 return std::tuple_cat(storage<Index>()->get_as_tuple(entity)...);
385 }
386 }
387
388 [[nodiscard]] auto each() const noexcept -> iterable {
389 return {base_type::begin(), base_type::end()};
390 }
391
392private:
393
394 template<std::size_t... Index>
395 [[nodiscard]] auto _get(const entity_type entity, std::index_sequence<Index...>) const noexcept {
396 return std::tuple_cat(storage<Index>()->get_as_tuple(entity)...);
397 }
398
399}; // class basic_view
400
401template<typename Get>
402class basic_view<get_t<Get>, exclude_t<>> : public detail::basic_storage_view<typename Get::base_type, Get::storage_policy> {
403
405
406public:
407
408 using common_type = typename base_type::common_type;
409 using entity_type = typename base_type::entity_type;
410 using size_type = typename base_type::size_type;
411 using difference_type = std::ptrdiff_t;
412 using iterator = typename base_type::iterator;
413 using reverse_iterator = typename base_type::reverse_iterator;
414 using iterable = std::conditional_t<Get::storage_policy == deletion_policy::in_place, memory::iterable_adaptor<detail::extended_view_iterator<iterator, Get>>, decltype(std::declval<Get>().each())>;
415
416 basic_view() noexcept
417 : base_type{} { }
418
419 basic_view(Get& value) noexcept
420 : base_type{&value} { }
421
422 basic_view(std::tuple<Get&> value) noexcept
423 : basic_view{std::get<0>(value)} {}
424
425 template<typename Type = typename Get::element_type>
426 requires (std::is_same_v<std::remove_const_t<Type>, typename Get::element_type>)
427 [[nodiscard]] auto* storage() const noexcept {
428 return storage<0>();
429 }
430
431 template<std::size_t Index>
432 requires (Index == 0u)
433 [[nodiscard]] auto* storage() const noexcept {
434 return static_cast<Get*>(const_cast<utility::constness_as_t<common_type, Get>*>(base_type::handle()));
435 }
436
437 void set_storage(Get& element) noexcept {
438 set_storage<0>(element);
439 }
440
441 template<std::size_t Index>
442 requires (Index == 0u)
443 void set_storage(Get& element) noexcept {
444 *this = basic_view{element};
445 }
446
447 [[nodiscard]] auto operator->() const noexcept -> Get* {
448 return storage();
449 }
450
451 [[nodiscard]] auto operator[](const entity_type entity) const -> decltype(auto) {
452 return storage()->get(entity);
453 }
454
455 template<typename Element>
456 requires (std::is_same_v<std::remove_const_t<Element>, typename Get::element_type>)
457 [[nodiscard]] auto get(const entity_type entity) const -> decltype(auto) {
458 return get<0>(entity);
459 }
460
461 template<std::size_t... Index>
462 [[nodiscard]] auto get(const entity_type entity) const -> decltype(auto) {
463 if constexpr(sizeof...(Index) == 0) {
464 return storage()->get_as_tuple(entity);
465 } else {
466 return storage<Index...>()->get(entity);
467 }
468 }
469
470 [[nodiscard]] auto each() const noexcept -> iterable {
471 if constexpr(Get::storage_policy == deletion_policy::swap_and_pop || Get::storage_policy == deletion_policy::swap_only) {
472 return base_type::handle() ? storage()->each() : iterable{};
473 } else {
474 static_assert(Get::storage_policy == deletion_policy::in_place, "Unexpected storage policy");
475 return iterable{base_type::begin(), base_type::end()};
476 }
477 }
478
479}; // class basic_view
480
481template<typename... Type>
482basic_view(Type&...storage) -> basic_view<get_t<Type...>, exclude_t<>>;
483
484template<typename... Get, typename... Exclude>
485basic_view(std::tuple<Get&...>, std::tuple<Exclude&...> = {}) -> basic_view<get_t<Get...>, exclude_t<Exclude...>>;
486
487} // namespace sbx::ecs
488
489#endif // LIBSBX_VIEW_HPP_
Definition: view.hpp:318
Definition: view.hpp:147
Definition: view_iterator.hpp:137
Definition: view_iterator.hpp:58
Definition: iterable_adaptor.hpp:11
deletion_policy
Deletion behavior policy for sparse sets.
Definition: sparse_set.hpp:56
Definition: view.hpp:310
Definition: view.hpp:302
Definition: type_list.hpp:10