|
|
@@ -22,7 +22,7 @@ namespace entt {
|
|
|
* @brief Basic storage implementation.
|
|
|
*
|
|
|
* This class is a refinement of a sparse set that associates an object to an
|
|
|
- * entity. The main purpose of this class is to use sparse sets to store
|
|
|
+ * entity. The main purpose of this class is to extend sparse sets to store
|
|
|
* components in a registry. It guarantees fast access both to the elements and
|
|
|
* to the entities.
|
|
|
*
|
|
|
@@ -36,19 +36,24 @@ namespace entt {
|
|
|
* iterate directly the internal packed array (see `raw` and `size` member
|
|
|
* functions for that). Use `begin` and `end` instead.
|
|
|
*
|
|
|
+ * @warning
|
|
|
+ * Empty types aren't explicitly instantiated. Temporary objects are returned in
|
|
|
+ * place of the instances of the components and raw access isn't available for
|
|
|
+ * them.
|
|
|
+ *
|
|
|
* @sa sparse_set<Entity>
|
|
|
*
|
|
|
* @tparam Entity A valid entity type (see entt_traits for more details).
|
|
|
* @tparam Type Type of objects assigned to the entities.
|
|
|
*/
|
|
|
-template<typename Entity, typename Type>
|
|
|
-class storage: public sparse_set<Entity> {
|
|
|
+template<typename Entity, typename Type, typename = std::void_t<>>
|
|
|
+class basic_storage: public sparse_set<Entity> {
|
|
|
using underlying_type = sparse_set<Entity>;
|
|
|
using traits_type = entt_traits<Entity>;
|
|
|
|
|
|
- template<bool Const, bool = std::is_empty_v<Type>>
|
|
|
+ template<bool Const>
|
|
|
class iterator {
|
|
|
- friend class storage<Entity, Type>;
|
|
|
+ friend class basic_storage<Entity, Type>;
|
|
|
|
|
|
using instance_type = std::conditional_t<Const, const std::vector<Type>, std::vector<Type>>;
|
|
|
using index_type = typename traits_type::difference_type;
|
|
|
@@ -59,9 +64,9 @@ class storage: public sparse_set<Entity> {
|
|
|
|
|
|
public:
|
|
|
using difference_type = index_type;
|
|
|
- using value_type = std::conditional_t<Const, const Type, Type>;
|
|
|
- using pointer = value_type *;
|
|
|
- using reference = value_type &;
|
|
|
+ using value_type = Type;
|
|
|
+ using pointer = std::conditional_t<Const, const value_type *, value_type *>;
|
|
|
+ using reference = std::conditional_t<Const, const value_type &, value_type &>;
|
|
|
using iterator_category = std::random_access_iterator_tag;
|
|
|
|
|
|
iterator() ENTT_NOEXCEPT = default;
|
|
|
@@ -148,106 +153,6 @@ class storage: public sparse_set<Entity> {
|
|
|
index_type index;
|
|
|
};
|
|
|
|
|
|
- template<bool Const>
|
|
|
- class iterator<Const, true> {
|
|
|
- friend class storage<Entity, Type>;
|
|
|
-
|
|
|
- using instance_type = std::conditional_t<Const, const Type, Type>;
|
|
|
- using index_type = typename traits_type::difference_type;
|
|
|
-
|
|
|
- iterator(instance_type *ref, const index_type idx) ENTT_NOEXCEPT
|
|
|
- : instance{ref}, index{idx}
|
|
|
- {}
|
|
|
-
|
|
|
- public:
|
|
|
- using difference_type = index_type;
|
|
|
- using value_type = std::conditional_t<Const, const Type, Type>;
|
|
|
- using pointer = value_type *;
|
|
|
- using reference = value_type &;
|
|
|
- using iterator_category = std::random_access_iterator_tag;
|
|
|
-
|
|
|
- iterator() ENTT_NOEXCEPT = default;
|
|
|
-
|
|
|
- iterator & operator++() ENTT_NOEXCEPT {
|
|
|
- return --index, *this;
|
|
|
- }
|
|
|
-
|
|
|
- iterator operator++(int) ENTT_NOEXCEPT {
|
|
|
- iterator orig = *this;
|
|
|
- return ++(*this), orig;
|
|
|
- }
|
|
|
-
|
|
|
- iterator & operator--() ENTT_NOEXCEPT {
|
|
|
- return ++index, *this;
|
|
|
- }
|
|
|
-
|
|
|
- iterator operator--(int) ENTT_NOEXCEPT {
|
|
|
- iterator orig = *this;
|
|
|
- return --(*this), orig;
|
|
|
- }
|
|
|
-
|
|
|
- iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
|
|
|
- index -= value;
|
|
|
- return *this;
|
|
|
- }
|
|
|
-
|
|
|
- iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
|
|
- return iterator{instance, index-value};
|
|
|
- }
|
|
|
-
|
|
|
- inline iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
|
|
|
- return (*this += -value);
|
|
|
- }
|
|
|
-
|
|
|
- inline iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
|
|
- return (*this + -value);
|
|
|
- }
|
|
|
-
|
|
|
- difference_type operator-(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
- return other.index - index;
|
|
|
- }
|
|
|
-
|
|
|
- reference operator[](const difference_type) const ENTT_NOEXCEPT {
|
|
|
- return *instance;
|
|
|
- }
|
|
|
-
|
|
|
- bool operator==(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
- return other.index == index;
|
|
|
- }
|
|
|
-
|
|
|
- inline bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
- return !(*this == other);
|
|
|
- }
|
|
|
-
|
|
|
- bool operator<(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
- return index > other.index;
|
|
|
- }
|
|
|
-
|
|
|
- bool operator>(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
- return index < other.index;
|
|
|
- }
|
|
|
-
|
|
|
- inline bool operator<=(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
- return !(*this > other);
|
|
|
- }
|
|
|
-
|
|
|
- inline bool operator>=(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
- return !(*this < other);
|
|
|
- }
|
|
|
-
|
|
|
- pointer operator->() const ENTT_NOEXCEPT {
|
|
|
- return instance;
|
|
|
- }
|
|
|
-
|
|
|
- inline reference operator*() const ENTT_NOEXCEPT {
|
|
|
- return *operator->();
|
|
|
- }
|
|
|
-
|
|
|
- private:
|
|
|
- instance_type *instance;
|
|
|
- index_type index;
|
|
|
- };
|
|
|
-
|
|
|
public:
|
|
|
/*! @brief Type of the objects associated with the entities. */
|
|
|
using object_type = Type;
|
|
|
@@ -270,25 +175,13 @@ public:
|
|
|
*/
|
|
|
void reserve(const size_type cap) override {
|
|
|
underlying_type::reserve(cap);
|
|
|
-
|
|
|
- if constexpr(!std::is_empty_v<object_type>) {
|
|
|
- instances.reserve(cap);
|
|
|
- }
|
|
|
+ instances.reserve(cap);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * @brief Requests the removal of unused capacity.
|
|
|
- *
|
|
|
- * @note
|
|
|
- * Empty components aren't explicitly instantiated. Only one instance of the
|
|
|
- * given type is created. Therefore, this function does nothing.
|
|
|
- */
|
|
|
+ /*! @brief Requests the removal of unused capacity. */
|
|
|
void shrink_to_fit() override {
|
|
|
underlying_type::shrink_to_fit();
|
|
|
-
|
|
|
- if constexpr(!std::is_empty_v<object_type>) {
|
|
|
- instances.shrink_to_fit();
|
|
|
- }
|
|
|
+ instances.shrink_to_fit();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -304,19 +197,10 @@ public:
|
|
|
* performance boost but less guarantees. Use `begin` and `end` if you want
|
|
|
* to iterate the storage in the expected order.
|
|
|
*
|
|
|
- * @note
|
|
|
- * Empty components aren't explicitly instantiated. Only one instance of the
|
|
|
- * given type is created. Therefore, this function always returns a pointer
|
|
|
- * to that instance.
|
|
|
- *
|
|
|
* @return A pointer to the array of objects.
|
|
|
*/
|
|
|
const object_type * raw() const ENTT_NOEXCEPT {
|
|
|
- if constexpr(std::is_empty_v<object_type>) {
|
|
|
- return &instances;
|
|
|
- } else {
|
|
|
- return instances.data();
|
|
|
- }
|
|
|
+ return instances.data();
|
|
|
}
|
|
|
|
|
|
/*! @copydoc raw */
|
|
|
@@ -392,13 +276,8 @@ public:
|
|
|
* @param entt A valid entity identifier.
|
|
|
* @return The object associated with the entity.
|
|
|
*/
|
|
|
- const object_type & get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
|
|
- if constexpr(std::is_empty_v<object_type>) {
|
|
|
- ENTT_ASSERT(underlying_type::has(entt));
|
|
|
- return instances;
|
|
|
- } else {
|
|
|
- return instances[underlying_type::get(entt)];
|
|
|
- }
|
|
|
+ const object_type & get(const entity_type entt) const ENTT_NOEXCEPT {
|
|
|
+ return instances[underlying_type::get(entt)];
|
|
|
}
|
|
|
|
|
|
/*! @copydoc get */
|
|
|
@@ -412,11 +291,7 @@ public:
|
|
|
* @return The object associated with the entity, if any.
|
|
|
*/
|
|
|
const object_type * try_get(const entity_type entt) const ENTT_NOEXCEPT {
|
|
|
- if constexpr(std::is_empty_v<object_type>) {
|
|
|
- return underlying_type::has(entt) ? &instances : nullptr;
|
|
|
- } else {
|
|
|
- return underlying_type::has(entt) ? (instances.data() + underlying_type::get(entt)) : nullptr;
|
|
|
- }
|
|
|
+ return underlying_type::has(entt) ? (instances.data() + underlying_type::get(entt)) : nullptr;
|
|
|
}
|
|
|
|
|
|
/*! @copydoc try_get */
|
|
|
@@ -443,21 +318,16 @@ public:
|
|
|
* @return The object associated with the entity.
|
|
|
*/
|
|
|
template<typename... Args>
|
|
|
- object_type & construct(const entity_type entt, [[maybe_unused]] Args &&... args) {
|
|
|
- if constexpr(std::is_empty_v<object_type>) {
|
|
|
- underlying_type::construct(entt);
|
|
|
- return instances;
|
|
|
+ object_type & construct(const entity_type entt, Args &&... args) {
|
|
|
+ if constexpr(std::is_aggregate_v<object_type>) {
|
|
|
+ instances.emplace_back(Type{std::forward<Args>(args)...});
|
|
|
} else {
|
|
|
- if constexpr(std::is_aggregate_v<object_type>) {
|
|
|
- instances.emplace_back(Type{std::forward<Args>(args)...});
|
|
|
- } else {
|
|
|
- instances.emplace_back(std::forward<Args>(args)...);
|
|
|
- }
|
|
|
-
|
|
|
- // entity goes after component in case constructor throws
|
|
|
- underlying_type::construct(entt);
|
|
|
- return instances.back();
|
|
|
+ instances.emplace_back(std::forward<Args>(args)...);
|
|
|
}
|
|
|
+
|
|
|
+ // entity goes after component in case constructor throws
|
|
|
+ underlying_type::construct(entt);
|
|
|
+ return instances.back();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -466,11 +336,6 @@ public:
|
|
|
*
|
|
|
* The object type must be at least default constructible.
|
|
|
*
|
|
|
- * @note
|
|
|
- * Empty components aren't explicitly instantiated. Only one instance of the
|
|
|
- * given type is created. Therefore, this function always returns a pointer
|
|
|
- * to that instance.
|
|
|
- *
|
|
|
* @warning
|
|
|
* Attempting to assign an entity that already belongs to the storage
|
|
|
* results in undefined behavior.<br/>
|
|
|
@@ -485,17 +350,12 @@ public:
|
|
|
*/
|
|
|
template<typename It>
|
|
|
object_type * batch(It first, It last) {
|
|
|
- if constexpr(std::is_empty_v<object_type>) {
|
|
|
- underlying_type::batch(first, last);
|
|
|
- return &instances;
|
|
|
- } else {
|
|
|
- static_assert(std::is_default_constructible_v<object_type>);
|
|
|
- const auto skip = instances.size();
|
|
|
- instances.insert(instances.end(), last-first, {});
|
|
|
- // entity goes after component in case constructor throws
|
|
|
- underlying_type::batch(first, last);
|
|
|
- return instances.data() + skip;
|
|
|
- }
|
|
|
+ static_assert(std::is_default_constructible_v<object_type>);
|
|
|
+ const auto skip = instances.size();
|
|
|
+ instances.insert(instances.end(), last-first, {});
|
|
|
+ // entity goes after component in case constructor throws
|
|
|
+ underlying_type::batch(first, last);
|
|
|
+ return instances.data() + skip;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -510,16 +370,13 @@ public:
|
|
|
* @param entt A valid entity identifier.
|
|
|
*/
|
|
|
void destroy(const entity_type entt) override {
|
|
|
- if constexpr(!std::is_empty_v<object_type>) {
|
|
|
- std::swap(instances[underlying_type::get(entt)], instances.back());
|
|
|
- instances.pop_back();
|
|
|
- }
|
|
|
-
|
|
|
+ std::swap(instances[underlying_type::get(entt)], instances.back());
|
|
|
+ instances.pop_back();
|
|
|
underlying_type::destroy(entt);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * @brief Sort components according to the given comparison function.
|
|
|
+ * @brief Sort instances according to the given comparison function.
|
|
|
*
|
|
|
* Sort the elements so that iterating the storage with a couple of
|
|
|
* iterators returns them in the expected order. See `begin` and `end` for
|
|
|
@@ -549,10 +406,6 @@ public:
|
|
|
* parameters to this member function.
|
|
|
*
|
|
|
* @note
|
|
|
- * Empty components aren't explicitly instantiated. Therefore, the
|
|
|
- * comparison function must necessarily accept entity identifiers.
|
|
|
- *
|
|
|
- * @note
|
|
|
* Attempting to iterate elements using a raw pointer returned by a call to
|
|
|
* either `data` or `raw` gives no guarantees on the order, even though
|
|
|
* `sort` has been invoked.
|
|
|
@@ -588,11 +441,7 @@ public:
|
|
|
while(curr != next) {
|
|
|
const auto lhs = copy[curr];
|
|
|
const auto rhs = copy[next];
|
|
|
-
|
|
|
- if constexpr(!std::is_empty_v<object_type>) {
|
|
|
- std::swap(instances[lhs], instances[rhs]);
|
|
|
- }
|
|
|
-
|
|
|
+ std::swap(instances[lhs], instances[rhs]);
|
|
|
underlying_type::swap(lhs, rhs);
|
|
|
copy[curr] = curr;
|
|
|
curr = next;
|
|
|
@@ -602,14 +451,13 @@ public:
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * @brief Sort components according to the order of the entities in another
|
|
|
+ * @brief Sort instances according to the order of the entities in another
|
|
|
* sparse set.
|
|
|
*
|
|
|
* Entities that are part of both the storage are ordered internally
|
|
|
* according to the order they have in `other`. All the other entities goes
|
|
|
* to the end of the list and there are no guarantess on their order.
|
|
|
- * Components are sorted according to the entities to which they
|
|
|
- * belong.<br/>
|
|
|
+ * Instances are sorted according to the entities to which they belong.<br/>
|
|
|
* In other terms, this function can be used to impose the same order on two
|
|
|
* sets by using one of them as a master and the other one as a slave.
|
|
|
*
|
|
|
@@ -625,46 +473,252 @@ public:
|
|
|
* @param other The sparse sets that imposes the order of the entities.
|
|
|
*/
|
|
|
void respect(const sparse_set<Entity> &other) ENTT_NOEXCEPT override {
|
|
|
- if constexpr(std::is_empty_v<object_type>) {
|
|
|
- underlying_type::respect(other);
|
|
|
- } else {
|
|
|
- const auto to = other.end();
|
|
|
- auto from = other.begin();
|
|
|
-
|
|
|
- size_type pos = underlying_type::size() - 1;
|
|
|
- const auto *local = underlying_type::data();
|
|
|
+ const auto to = other.end();
|
|
|
+ auto from = other.begin();
|
|
|
|
|
|
- while(pos && from != to) {
|
|
|
- const auto curr = *from;
|
|
|
+ size_type pos = underlying_type::size() - 1;
|
|
|
+ const auto *local = underlying_type::data();
|
|
|
|
|
|
- if(underlying_type::has(curr)) {
|
|
|
- if(curr != *(local + pos)) {
|
|
|
- auto candidate = underlying_type::get(curr);
|
|
|
- std::swap(instances[pos], instances[candidate]);
|
|
|
- underlying_type::swap(pos, candidate);
|
|
|
- }
|
|
|
+ while(pos && from != to) {
|
|
|
+ const auto curr = *from;
|
|
|
|
|
|
- --pos;
|
|
|
+ if(underlying_type::has(curr)) {
|
|
|
+ if(curr != *(local + pos)) {
|
|
|
+ auto candidate = underlying_type::get(curr);
|
|
|
+ std::swap(instances[pos], instances[candidate]);
|
|
|
+ underlying_type::swap(pos, candidate);
|
|
|
}
|
|
|
|
|
|
- ++from;
|
|
|
+ --pos;
|
|
|
}
|
|
|
+
|
|
|
+ ++from;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/*! @brief Resets a storage. */
|
|
|
void reset() override {
|
|
|
underlying_type::reset();
|
|
|
+ instances.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ std::vector<object_type> instances;
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+/*! @copydoc basic_storage */
|
|
|
+template<typename Entity, typename Type>
|
|
|
+class basic_storage<Entity, Type, std::enable_if_t<std::is_empty_v<Type>>>: public sparse_set<Entity> {
|
|
|
+ using underlying_type = sparse_set<Entity>;
|
|
|
+ using traits_type = entt_traits<Entity>;
|
|
|
+
|
|
|
+ class iterator {
|
|
|
+ friend class basic_storage<Entity, Type>;
|
|
|
+
|
|
|
+ using index_type = typename traits_type::difference_type;
|
|
|
+
|
|
|
+ iterator(const index_type idx) ENTT_NOEXCEPT
|
|
|
+ : index{idx}
|
|
|
+ {}
|
|
|
+
|
|
|
+ public:
|
|
|
+ using difference_type = index_type;
|
|
|
+ using value_type = Type;
|
|
|
+ using pointer = const value_type *;
|
|
|
+ using reference = value_type;
|
|
|
+ using iterator_category = std::input_iterator_tag;
|
|
|
|
|
|
- if constexpr(!std::is_empty_v<object_type>) {
|
|
|
- instances.clear();
|
|
|
+ iterator() ENTT_NOEXCEPT = default;
|
|
|
+
|
|
|
+ iterator & operator++() ENTT_NOEXCEPT {
|
|
|
+ return --index, *this;
|
|
|
+ }
|
|
|
+
|
|
|
+ iterator operator++(int) ENTT_NOEXCEPT {
|
|
|
+ iterator orig = *this;
|
|
|
+ return ++(*this), orig;
|
|
|
}
|
|
|
+
|
|
|
+ iterator & operator--() ENTT_NOEXCEPT {
|
|
|
+ return ++index, *this;
|
|
|
+ }
|
|
|
+
|
|
|
+ iterator operator--(int) ENTT_NOEXCEPT {
|
|
|
+ iterator orig = *this;
|
|
|
+ return --(*this), orig;
|
|
|
+ }
|
|
|
+
|
|
|
+ iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
|
|
|
+ index -= value;
|
|
|
+ return *this;
|
|
|
+ }
|
|
|
+
|
|
|
+ iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
|
|
+ return iterator{index-value};
|
|
|
+ }
|
|
|
+
|
|
|
+ inline iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
|
|
|
+ return (*this += -value);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
|
|
+ return (*this + -value);
|
|
|
+ }
|
|
|
+
|
|
|
+ difference_type operator-(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
+ return other.index - index;
|
|
|
+ }
|
|
|
+
|
|
|
+ reference operator[](const difference_type) const ENTT_NOEXCEPT {
|
|
|
+ return {};
|
|
|
+ }
|
|
|
+
|
|
|
+ bool operator==(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
+ return other.index == index;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
+ return !(*this == other);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool operator<(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
+ return index > other.index;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool operator>(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
+ return index < other.index;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool operator<=(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
+ return !(*this > other);
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool operator>=(const iterator &other) const ENTT_NOEXCEPT {
|
|
|
+ return !(*this < other);
|
|
|
+ }
|
|
|
+
|
|
|
+ pointer operator->() const ENTT_NOEXCEPT {
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline reference operator*() const ENTT_NOEXCEPT {
|
|
|
+ return {};
|
|
|
+ }
|
|
|
+
|
|
|
+ private:
|
|
|
+ index_type index;
|
|
|
+ };
|
|
|
+
|
|
|
+public:
|
|
|
+ /*! @brief Type of the objects associated with the entities. */
|
|
|
+ using object_type = Type;
|
|
|
+ /*! @brief Underlying entity identifier. */
|
|
|
+ using entity_type = typename underlying_type::entity_type;
|
|
|
+ /*! @brief Unsigned integer type. */
|
|
|
+ using size_type = typename underlying_type::size_type;
|
|
|
+ /*! @brief Random access iterator type. */
|
|
|
+ using iterator_type = iterator;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @brief Direct access to the array of objects.
|
|
|
+ *
|
|
|
+ * @note
|
|
|
+ * Empty types aren't explicitly instantiated. Therefore, this function
|
|
|
+ * always returns a null pointer.
|
|
|
+ *
|
|
|
+ * @return A pointer to the array of objects.
|
|
|
+ */
|
|
|
+ const object_type * raw() const ENTT_NOEXCEPT {
|
|
|
+ return nullptr;
|
|
|
}
|
|
|
|
|
|
-private:
|
|
|
- std::conditional_t<std::is_empty_v<object_type>, object_type, std::vector<object_type>> instances;
|
|
|
+ /**
|
|
|
+ * @brief Returns an iterator to the beginning.
|
|
|
+ *
|
|
|
+ * The returned iterator points to the first instance of the given type. If
|
|
|
+ * the storage is empty, the returned iterator will be equal to `end()`.
|
|
|
+ *
|
|
|
+ * @note
|
|
|
+ * Input iterators stay true to the order imposed by a call to either `sort`
|
|
|
+ * or `respect`.
|
|
|
+ *
|
|
|
+ * @return An iterator to the first instance of the given type.
|
|
|
+ */
|
|
|
+ iterator_type cbegin() const ENTT_NOEXCEPT {
|
|
|
+ const typename traits_type::difference_type pos = underlying_type::size();
|
|
|
+ return iterator_type{pos};
|
|
|
+ }
|
|
|
+
|
|
|
+ /*! @copydoc cbegin */
|
|
|
+ inline iterator_type begin() const ENTT_NOEXCEPT {
|
|
|
+ return cbegin();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @brief Returns an iterator to the end.
|
|
|
+ *
|
|
|
+ * The returned iterator points to the element following the last instance
|
|
|
+ * of the given type. Attempting to dereference the returned iterator
|
|
|
+ * results in undefined behavior.
|
|
|
+ *
|
|
|
+ * @note
|
|
|
+ * Input iterators stay true to the order imposed by a call to either `sort`
|
|
|
+ * or `respect`.
|
|
|
+ *
|
|
|
+ * @return An iterator to the element following the last instance of the
|
|
|
+ * given type.
|
|
|
+ */
|
|
|
+ iterator_type cend() const ENTT_NOEXCEPT {
|
|
|
+ return iterator_type{};
|
|
|
+ }
|
|
|
+
|
|
|
+ /*! @copydoc cend */
|
|
|
+ inline iterator_type end() const ENTT_NOEXCEPT {
|
|
|
+ return cend();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @brief Returns the object associated with an entity.
|
|
|
+ *
|
|
|
+ * @note
|
|
|
+ * Empty types aren't explicitly instantiated. Therefore, this function
|
|
|
+ * always returns a temporary object.
|
|
|
+ *
|
|
|
+ * @warning
|
|
|
+ * Attempting to use an entity that doesn't belong to the storage results in
|
|
|
+ * undefined behavior.<br/>
|
|
|
+ * An assertion will abort the execution at runtime in debug mode if the
|
|
|
+ * storage doesn't contain the given entity.
|
|
|
+ *
|
|
|
+ * @param entt A valid entity identifier.
|
|
|
+ * @return The object associated with the entity.
|
|
|
+ */
|
|
|
+ object_type get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
|
|
+ ENTT_ASSERT(underlying_type::has(entt));
|
|
|
+ return {};
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @brief Returns a pointer to the object associated with an entity, if any.
|
|
|
+ *
|
|
|
+ * @note
|
|
|
+ * Empty types aren't explicitly instantiated. Therefore, this function
|
|
|
+ * always returns a null pointer.
|
|
|
+ *
|
|
|
+ * @param entt A valid entity identifier.
|
|
|
+ * @return The object associated with the entity, if any.
|
|
|
+ */
|
|
|
+ const object_type * try_get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
|
|
+ ENTT_ASSERT(underlying_type::has(entt));
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
+/*! @copydoc basic_storage */
|
|
|
+template<typename Entity, typename Type>
|
|
|
+struct storage: basic_storage<Entity, Type> {};
|
|
|
+
|
|
|
|
|
|
}
|
|
|
|