ECSTASY
All in the name
Loading...
Searching...
No Matches
Query.hpp
Go to the documentation of this file.
1
11
12#ifndef ECSTASY_QUERY_QUERY_HPP_
13#define ECSTASY_QUERY_QUERY_HPP_
14
15#include <algorithm>
16#include <functional>
17#include <thread>
18
23#include "util/BitSet.hpp"
24#include "util/meta/Traits.hpp"
25#include "util/meta/index.hpp"
26
27namespace ecstasy::query
28{
29 class Entities;
30 class IStorage;
31
32 // clang-format off
45 // clang-format on
46 template <typename Storages, typename Conditions = void, bool AutoLock = false>
48
49 // clang-format off
61 // clang-format on
62 template <Queryable First, Queryable... Others, typename... Conditions, bool AutoLock>
63 class QueryImplementation<util::meta::Traits<First, Others...>, util::meta::Traits<Conditions...>, AutoLock> {
64 public:
67 using Queryables =
69
76 class Iterator {
77 public:
83
85 // All iterators must be constructible, copy-constructible, copy-assignable, destructible and swappable.///
87
94 explicit Iterator() noexcept
95 {
96 }
97
108 explicit Iterator(util::BitSet const &mask, Queryables const &storages, std::size_t pos) noexcept(
109 sizeof...(Conditions) == 0)
110 : _mask(std::cref(mask)), _storages(std::cref(storages)), _pos(pos)
111 {
112 if constexpr (sizeof...(Conditions) != 0)
113 applyConditions();
114 }
115
122 Iterator(Iterator const &) noexcept = default;
123
132 [[nodiscard]] Iterator &operator=(Iterator const &) noexcept = default;
133
140 Iterator(Iterator &&) noexcept = default;
141
150 [[nodiscard]] Iterator &operator=(Iterator &&) noexcept = default;
151
164 [[nodiscard]] constexpr bool operator==(Iterator const &other) const noexcept
165 {
166 return this->_pos == other._pos;
167 }
168
181 [[nodiscard]] constexpr bool operator!=(Iterator const &other) const noexcept
182 {
183 return this->_pos != other._pos;
184 }
185
194 [[nodiscard]] value_type operator*() const
195 {
196 return this->get_components(std::make_index_sequence<(sizeof...(Others)) + 1>());
197 }
198
207 [[nodiscard]] value_type operator->() const
208 {
209 return *this;
210 }
211
222 Iterator &operator++()
223 {
224 this->_pos = this->_mask.get().firstSet(this->_pos + 1);
225 if constexpr (sizeof...(Conditions) != 0)
226 applyConditions();
227 return *this;
228 }
229
241 [[nodiscard]] Iterator operator++(int)
242 {
243 Iterator result = *this;
244
245 return ++result;
246 }
247
256 [[nodiscard]] constexpr size_t getPosition() const noexcept
257 {
258 return _pos;
259 }
260
261 private:
268
279 template <QConditionConst Condition>
280 bool checkCondition() const
281 {
282 return Condition::test();
283 }
284
295 template <QConditionLeft Condition>
296 bool checkCondition() const
297 {
298 static_assert(
302 "Condition left operand must be included in the queryable types.");
303 auto &storage = std::get<
304 util::meta::index_v<typename Condition::Left, std::remove_reference_t<queryable_data_t<First>>,
306
307 return Condition::test(getQueryableData(storage, _pos));
308 }
309
320 template <QConditionRight Condition>
321 bool checkCondition() const
322 {
323 static_assert(
327 "Condition right operand must be included in the queryable types.");
328 auto &storage = std::get<
329 util::meta::index_v<typename Condition::Right, std::remove_reference_t<queryable_data_t<First>>,
331
332 return Condition::test(getQueryableData(storage, _pos));
333 }
334
345 template <QConditionLeftRight Condition>
346 bool checkCondition() const
347 {
348 static_assert(
352 "Condition left operand must be included in the queryable types.");
353 static_assert(
357 "Condition right operand must be included in the queryable types.");
358 auto &storageLeft = std::get<
359 util::meta::index_v<typename Condition::Left, std::remove_reference_t<queryable_data_t<First>>,
361 auto &storageRight = std::get<
362 util::meta::index_v<typename Condition::Right, std::remove_reference_t<queryable_data_t<First>>,
364
365 return Condition::test(getQueryableData(storageLeft, _pos), getQueryableData(storageRight, _pos));
366 }
367
377 inline void applyConditions()
378 {
379 if constexpr (sizeof...(Conditions) != 0) {
380 while (this->_pos != this->_mask.get().size() - 1 && !(true & ... & checkCondition<Conditions>()))
381 ++*this;
382 }
383 }
384
395 template <size_t... Indices>
397 {
398 return {getQueryableData(std::get<Indices>(_storages.get()), _pos)...};
399 }
400 };
401
411 QueryImplementation(util::BitSet &mask, const Queryables &storages) : _mask(mask), _storages(storages)
412 {
413 // push a sentinel bit at the end.
414 this->_mask.push(true);
415 this->_begin = this->_mask.firstSet();
416 }
417
427 QueryImplementation(First &first, Others &...others) : _storages(first, others...)
428 {
430 if constexpr (is_queryable_with_adjust_v<First>
431 || std::disjunction_v<is_queryable_with_adjust<Others>...>) {
432 size_t maxSize = std::max({getQueryableMask(first).size(), getQueryableMask(others).size()...});
433
434 adjustMask(first, maxSize);
435 (adjustMask(others, maxSize), ...);
436 }
437 this->_mask = (util::BitSet(getQueryableMask(first)) &= ... &= getQueryableMask(others));
438
439 // push a sentinel bit at the end.
440 this->_mask.push(true);
441 this->_begin = this->_mask.firstSet();
442 }
443
453 [[nodiscard]] Iterator begin() const noexcept
454 {
455 return Iterator(this->_mask, this->_storages, this->_begin);
456 }
457
466 [[nodiscard]] Iterator end() const noexcept
467 {
468 return Iterator(this->_mask, this->_storages, this->_mask.size() - 1);
469 }
470
481 [[nodiscard]] constexpr const util::BitSet &getMask() const noexcept
482 {
483 return _mask;
484 }
485
498 [[nodiscard]] QueryData getQueryData(std::size_t index) const
499 {
500 return get_components(index, std::make_index_sequence<(sizeof...(Others)) + 1>());
501 }
502
521 template <typename F>
522 void splitThreads(std::size_t batchSize, F &&fct, bool wait = true)
523 {
524 Iterator end = this->end();
525 Iterator current = this->begin();
526 Iterator currentBatchStart = current;
527 size_t currentBatchSize = 0;
529
530 while (current != end) {
531 ++currentBatchSize;
532 // Find the next element (which may be the end sentinel)
533 ++current;
534 // Batch full, we start a new thread and reset the current batch informations
535 if (currentBatchSize == batchSize) {
536 threads.emplace_back(processBatch<F>, currentBatchStart, current, fct);
537 currentBatchSize = 0;
538 currentBatchStart = current;
539 }
540 }
541
542 if (currentBatchSize > 0)
543 threads.emplace_back(processBatch<F>, currentBatchStart, current, fct);
544
545 if (wait)
546 for (auto &thread : threads)
547 thread.join();
548 }
549
550 private:
556 size_t _begin;
557
570 template <typename F>
571 static void processBatch(Iterator start, Iterator end, F &&fct) noexcept(noexcept(fct(*start)))
572 {
573 for (auto i = start; i != end; ++i)
574 fct(*i);
575 }
576
587 template <size_t... Indices>
589 {
590 return {getQueryableData(std::get<Indices>(_storages), index)...};
591 }
592 };
593
603 template <Queryable First, Queryable... Others>
604 class Query : public QueryImplementation<util::meta::Traits<First, Others...>, util::meta::Traits<>> {
605 public:
616 : QueryImplementation<util::meta::Traits<First, Others...>, util::meta::Traits<>>(mask, storages)
617 {
618 }
619
629 Query(First &first, Others &...others)
630 : QueryImplementation<util::meta::Traits<First, Others...>, util::meta::Traits<>>(
631 first, std::forward<Others &>(others)...)
632 {
633 }
634 };
635
645 template <Queryable... Qs>
646 class ThreadSafeQuery : public QueryImplementation<util::meta::Traits<Qs...>, util::meta::Traits<>, true> {
647 public:
658 : QueryImplementation<util::meta::Traits<Qs...>, util::meta::Traits<>, true>(mask, storages)
659 {
660 }
661
670 ThreadSafeQuery(Qs &...others)
671 : QueryImplementation<util::meta::Traits<Qs...>, util::meta::Traits<>, true>(std::forward<Qs &>(others)...)
672 {
673 }
674 };
675
676} // namespace ecstasy::query
677
678#endif /* !QUERY_HPP_ */
Contains the concepts for queryable objects.
Defines a Queryable object type which has the adjustMask() implemented.
Helper types for parameter packs.
More high-level query class, wrapping the QueryImplementation.
Definition Query.hpp:604
Query(util::BitSet &mask, const std::tuple< First &, Others &... > &storages)
Construct a new Query from a bitmask already computed and a storages list.
Definition Query.hpp:615
Query(First &first, Others &...others)
Construct a new Query from a list of storages.
Definition Query.hpp:629
Iterator operator++(int)
Copies the iterator and increments the copy, please use pre-incrementation instead.
Definition Query.hpp:241
value_type operator*() const
Fetch the components corresponding to the entity at the current position.
Definition Query.hpp:194
value_type get_components(std::index_sequence< Indices... >) const
Get the components from the storages tuple.
Definition Query.hpp:396
value_type operator->() const
Fetch the components corresponding to the entity at the current position.
Definition Query.hpp:207
constexpr bool operator!=(Iterator const &other) const noexcept
Compare two iterators from the same Query.
Definition Query.hpp:181
Iterator(util::BitSet const &mask, Queryables const &storages, std::size_t pos) noexcept(sizeof...(Conditions)==0)
Construct a new Iterator.
Definition Query.hpp:108
static void processBatch(Iterator start, Iterator end, F &&fct) noexcept(noexcept(fct(*start)))
Adjust the mask of a queryable to the given size.
Definition Query.hpp:571
QueryImplementation(First &first, Others &...others)
Construct a new Query trying to match multiple storages on one entity.
Definition Query.hpp:427
QueryData getQueryData(std::size_t index) const
Query the components associated to the given entity.
Definition Query.hpp:498
QueryImplementation(util::BitSet &mask, const Queryables &storages)
Construct a new Query from a bitmask already computed and a storages list.
Definition Query.hpp:411
QueryData get_components(std::size_t index, std::index_sequence< Indices... >) const
Get the components from the storages tuple.
Definition Query.hpp:588
void splitThreads(std::size_t batchSize, F &&fct, bool wait=true)
Split the query in multiple batch of batchSize matching entities.
Definition Query.hpp:522
Query components presents in all given queryables.
Definition Query.hpp:47
More high-level query class, wrapping the QueryImplementation and locking all Lockable queryables.
Definition Query.hpp:646
ThreadSafeQuery(Qs &...others)
Construct a new Thread Safe Query from a list of storages.
Definition Query.hpp:670
ThreadSafeQuery(util::BitSet &mask, const std::tuple< Qs &... > &storages)
Construct a new Thread Safe Query from a bitmask already computed and a storages list.
Definition Query.hpp:657
Mimics the API of std::bitset but with the dynamic properties of std::vector<bool>
Definition BitSet.hpp:35
constexpr std::size_t size() const noexcept
Definition BitSet.hpp:87
Defines a type that can be queried.
Contains all the condition concepts.
Contains the condition resolution structures.
T disjunction_v
T emplace_back(T... args)
Get the index of the first occurence of a type in a list of types.
T max(T... args)
Namespace regrouping the internal ecstasy query system.
Definition Condition.hpp:18
typename queryable_data< Q >::type queryable_data_t
Alias for the query data type of a queryable object.
typename queryable_qualifiers< Q, AutoLock >::type queryable_qualifiers_t
Alias for the queryable type with the correct qualifiers.
constexpr queryable_data_t< Q > getQueryableData(Q &queryable, size_t index)
Get the query data at the given index.
constexpr const util::BitSet & getQueryableMask(const Q &queryable)
Get the mask of the queryable object.
constexpr void adjustMask(T &queryable, size_t maxSize) noexcept
Adjust the queryable mask if needed.
Namespace regrouping helpers used by ecstasy but not specific to ecstasy.
Definition Queryable.hpp:21
T cref(T... args)
Empty parameter pack helper type.
Definition Traits.hpp:28