diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index 366f32282..1ab89c58c 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -76,10 +76,17 @@ class basic_registry { {} template - Component & assign(basic_registry ®istry, const Entity entt, Args &&... args) { - auto &component = storage::construct(entt, std::forward(args)...); - on_construct.publish(registry, entt, component); - return component; + decltype(auto) assign(basic_registry ®istry, const Entity entt, Args &&... args) { + if constexpr(std::is_empty_v) { + Component component{std::forward(args)...}; + storage::construct(entt); + on_construct.publish(registry, entt, component); + return std::move(component); + } else { + auto &component = storage::construct(entt, std::forward(args)...); + on_construct.publish(registry, entt, component); + return component; + } } template @@ -87,9 +94,15 @@ class basic_registry { auto *component = storage::batch(first, last); if(!on_construct.empty()) { - std::for_each(first, last, [®istry, component, this](const auto entt) mutable { - on_construct.publish(registry, entt, *(component++)); - }); + if constexpr(std::is_empty_v) { + std::for_each(first, last, [®istry, component = Component{}, this](const auto entt) { + on_construct.publish(registry, entt, component); + }); + } else { + std::for_each(first, last, [®istry, component, this](const auto entt) mutable { + on_construct.publish(registry, entt, *(component++)); + }); + } } return component; @@ -101,10 +114,15 @@ class basic_registry { } template - Component & replace(basic_registry ®istry, const Entity entt, Args &&... args) { + decltype(auto) replace(basic_registry ®istry, const Entity entt, [[maybe_unused]] Args &&... args) { Component component{std::forward(args)...}; on_replace.publish(registry, entt, component); - return (storage::get(entt) = std::move(component)); + + if constexpr(std::is_empty_v) { + return std::move(component); + } else { + return (storage::get(entt) = std::move(component)); + } } }; @@ -145,25 +163,21 @@ class basic_registry { void maybe_valid_if(basic_registry ®, const Entity entt, const Args &...) { const auto cpools = std::make_tuple(reg.pool()...); - auto construct = [&cpools, entt, this]() { - const auto pos = this->owned++; - (std::swap(std::get *>(cpools)->get(entt), std::get *>(cpools)->raw()[pos]), ...); - (std::get *>(cpools)->swap(std::get *>(cpools)->sparse_set::get(entt), pos), ...); - }; - if constexpr(std::disjunction_v..., std::is_same...>) { if(((std::is_same_v || std::get *>(cpools)->has(entt)) && ...) && ((std::is_same_v || reg.pool()->has(entt)) && ...) && !(reg.pool()->has(entt) || ...)) { - construct(); + const auto pos = this->owned++; + (reg.swap(0, std::get *>(cpools), entt, pos), ...); } } else if constexpr(std::disjunction_v...>) { if((std::get *>(cpools)->has(entt) && ...) && (reg.pool()->has(entt) && ...) && ((std::is_same_v || !reg.pool()->has(entt)) && ...)) { - construct(); + const auto pos = this->owned++; + (reg.swap(0, std::get *>(cpools), entt, pos), ...); } } } @@ -174,8 +188,7 @@ class basic_registry { if(std::get<0>(cpools)->has(entt) && std::get<0>(cpools)->sparse_set::get(entt) < this->owned) { const auto pos = --this->owned; - (std::swap(std::get *>(cpools)->get(entt), std::get *>(cpools)->raw()[pos]), ...); - (std::get *>(cpools)->swap(std::get *>(cpools)->sparse_set::get(entt), pos), ...); + (reg.swap(0, std::get *>(cpools), entt, pos), ...); } } }; @@ -217,6 +230,18 @@ class basic_registry { ++available; } + template + inline auto swap(int, pool_type *cpool, const Entity entt, const std::size_t pos) + -> decltype(std::swap(cpool->get(entt), cpool->get(cpool->data()[pos])), void()) { + std::swap(cpool->get(entt), cpool->get(cpool->data()[pos])); + cpool->swap(cpool->sparse_set::get(entt), pos); + } + + template + inline void swap(char, pool_type *cpool, const Entity entt, const std::size_t pos) { + cpool->swap(cpool->sparse_set::get(entt), pos); + } + template inline const auto * pool() const ENTT_NOEXCEPT { const auto ctype = type(); @@ -694,7 +719,7 @@ public: * @return A reference to the newly created component. */ template - Component & assign(const entity_type entity, Args &&... args) { + decltype(auto) assign(const entity_type entity, [[maybe_unused]] Args &&... args) { ENTT_ASSERT(valid(entity)); return assure()->assign(*this, entity, std::forward(args)...); } @@ -797,7 +822,7 @@ public: * @return Reference to the component owned by the entity. */ template - Component & get_or_assign(const entity_type entity, Args &&... args) ENTT_NOEXCEPT { + decltype(auto) get_or_assign(const entity_type entity, Args &&... args) ENTT_NOEXCEPT { ENTT_ASSERT(valid(entity)); auto *cpool = assure(); auto *comp = cpool->try_get(entity); @@ -859,7 +884,7 @@ public: * @return A reference to the newly created component. */ template - Component & replace(const entity_type entity, Args &&... args) { + decltype(auto) replace(const entity_type entity, Args &&... args) { return pool()->replace(*this, entity, std::forward(args)...); } @@ -886,7 +911,7 @@ public: * @return A reference to the newly created component. */ template - Component & assign_or_replace(const entity_type entity, Args &&... args) { + decltype(auto) assign_or_replace(const entity_type entity, Args &&... args) { auto *cpool = assure(); return cpool->has(entity) ? cpool->replace(*this, entity, std::forward(args)...) : cpool->assign(*this, entity, std::forward(args)...); } @@ -1290,6 +1315,7 @@ public: inline entt::basic_group, Owned...> group(get_t, exclude_t = {}) { static_assert(sizeof...(Owned) + sizeof...(Get) > 0); static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1); + using handler_type = group_handler, type_list, Owned...>; const std::size_t extent[] = { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) }; @@ -1336,7 +1362,7 @@ public: }); // we cannot iterate backwards because we want to leave behind valid entities in case of owned types - std::for_each(cpool->data(), cpool->data() + cpool->size(), [curr, &cpools](const auto entity) { + std::for_each(cpool->data(), cpool->data() + cpool->size(), [curr, &cpools, this](const auto entity) { if((std::get *>(cpools)->has(entity) && ...) && (std::get *>(cpools)->has(entity) && ...) && !(std::get *>(cpools)->has(entity) || ...)) @@ -1345,8 +1371,7 @@ public: curr->construct(entity); } else { const auto pos = curr->owned++; - (std::swap(std::get *>(cpools)->get(entity), std::get *>(cpools)->raw()[pos]), ...); - (std::get *>(cpools)->swap(std::get *>(cpools)->sparse_set::get(entity), pos), ...); + (swap(0, std::get *>(cpools), entity, pos), ...); } } }); diff --git a/src/entt/entity/snapshot.hpp b/src/entt/entity/snapshot.hpp index 46ed5d343..5a64dc315 100644 --- a/src/entt/entity/snapshot.hpp +++ b/src/entt/entity/snapshot.hpp @@ -226,18 +226,19 @@ class basic_snapshot_loader { archive(length); while(length--) { + static constexpr auto destroyed = false; Entity entt{}; - Type instance{}; if constexpr(std::is_empty_v) { archive(entt); + force(*reg, entt, destroyed); + reg->template assign(args..., entt); } else { + Type instance{}; archive(entt, instance); + force(*reg, entt, destroyed); + reg->template assign(args..., entt, std::as_const(instance)); } - - static constexpr auto destroyed = false; - force(*reg, entt, destroyed); - reg->template assign(args..., entt, std::as_const(instance)); } } @@ -405,24 +406,25 @@ class basic_continuous_loader { } } - template - void assign(Archive &archive, Func func, Member Type:: *... member) { + template + void assign(Archive &archive, [[maybe_unused]] Member Type:: *... member) { Entity length{}; archive(length); while(length--) { Entity entt{}; - Other instance{}; if constexpr(std::is_empty_v) { archive(entt); + restore(entt); + reg->template assign_or_replace(map(entt)); } else { + Other instance{}; archive(entt, instance); + (update(instance, member), ...); + restore(entt); + reg->template assign_or_replace(map(entt), std::as_const(instance)); } - - restore(entt); - (update(instance, member), ...); - func(map(entt), instance); } } @@ -497,12 +499,8 @@ public: */ template basic_continuous_loader & component(Archive &archive, Member Type:: *... member) { - auto apply = [this](const auto entt, const auto &component) { - reg->template assign_or_replace>(entt, component); - }; - (reset(), ...); - (assign(archive, apply, member...), ...); + (assign(archive, member...), ...); return *this; } diff --git a/src/entt/entity/sparse_set.hpp b/src/entt/entity/sparse_set.hpp index f2a0591f2..f22d32643 100644 --- a/src/entt/entity/sparse_set.hpp +++ b/src/entt/entity/sparse_set.hpp @@ -61,9 +61,9 @@ class sparse_set { public: using difference_type = index_type; - using value_type = const Entity; - using pointer = value_type *; - using reference = value_type &; + using value_type = Entity; + using pointer = const value_type *; + using reference = const value_type &; using iterator_category = std::random_access_iterator_tag; iterator() ENTT_NOEXCEPT = default; diff --git a/src/entt/entity/storage.hpp b/src/entt/entity/storage.hpp index 839073875..d94b3eb07 100644 --- a/src/entt/entity/storage.hpp +++ b/src/entt/entity/storage.hpp @@ -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 * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Type Type of objects assigned to the entities. */ -template -class storage: public sparse_set { +template> +class basic_storage: public sparse_set { using underlying_type = sparse_set; using traits_type = entt_traits; - template> + template class iterator { - friend class storage; + friend class basic_storage; using instance_type = std::conditional_t, std::vector>; using index_type = typename traits_type::difference_type; @@ -59,9 +64,9 @@ class storage: public sparse_set { public: using difference_type = index_type; - using value_type = std::conditional_t; - using pointer = value_type *; - using reference = value_type &; + using value_type = Type; + using pointer = std::conditional_t; + using reference = std::conditional_t; using iterator_category = std::random_access_iterator_tag; iterator() ENTT_NOEXCEPT = default; @@ -148,106 +153,6 @@ class storage: public sparse_set { index_type index; }; - template - class iterator { - friend class storage; - - using instance_type = std::conditional_t; - 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; - 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) { - 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) { - 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) { - 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) { - 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) { - 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 - object_type & construct(const entity_type entt, [[maybe_unused]] Args &&... args) { - if constexpr(std::is_empty_v) { - underlying_type::construct(entt); - return instances; + object_type & construct(const entity_type entt, Args &&... args) { + if constexpr(std::is_aggregate_v) { + instances.emplace_back(Type{std::forward(args)...}); } else { - if constexpr(std::is_aggregate_v) { - instances.emplace_back(Type{std::forward(args)...}); - } else { - instances.emplace_back(std::forward(args)...); - } - - // entity goes after component in case constructor throws - underlying_type::construct(entt); - return instances.back(); + instances.emplace_back(std::forward(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.
@@ -485,17 +350,12 @@ public: */ template object_type * batch(It first, It last) { - if constexpr(std::is_empty_v) { - underlying_type::batch(first, last); - return &instances; - } else { - static_assert(std::is_default_constructible_v); - 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); + 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) { - 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) { - 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.
+ * Instances are sorted according to the entities to which they belong.
* 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,47 +473,253 @@ public: * @param other The sparse sets that imposes the order of the entities. */ void respect(const sparse_set &other) ENTT_NOEXCEPT override { - if constexpr(std::is_empty_v) { - underlying_type::respect(other); - } else { - const auto to = other.end(); - auto from = other.begin(); + const auto to = other.end(); + auto from = other.begin(); - size_type pos = underlying_type::size() - 1; - const auto *local = underlying_type::data(); + size_type pos = underlying_type::size() - 1; + const auto *local = underlying_type::data(); - while(pos && from != to) { - const auto curr = *from; + while(pos && from != to) { + const auto curr = *from; - 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); - } - - --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(); - - if constexpr(!std::is_empty_v) { - instances.clear(); - } + instances.clear(); } private: - std::conditional_t, object_type, std::vector> instances; + std::vector instances; }; +/*! @copydoc basic_storage */ +template +class basic_storage>>: public sparse_set { + using underlying_type = sparse_set; + using traits_type = entt_traits; + + class iterator { + friend class basic_storage; + + 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; + + 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; + } + + /** + * @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.
+ * 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 +struct storage: basic_storage {}; + + } diff --git a/src/entt/entity/view.hpp b/src/entt/entity/view.hpp index 35e8ba841..77817a0e1 100644 --- a/src/entt/entity/view.hpp +++ b/src/entt/entity/view.hpp @@ -161,20 +161,28 @@ class basic_view { return other; } + template + inline decltype(auto) get([[maybe_unused]] component_iterator_type it, [[maybe_unused]] pool_type *cpool, [[maybe_unused]] const Entity entt) const ENTT_NOEXCEPT { + if constexpr(std::is_same_v) { + return *it; + } else { + return cpool->get(entt); + } + } + template void each(type_list, Func func) const { const auto end = std::get *>(pools)->sparse_set::end(); auto begin = std::get *>(pools)->sparse_set::begin(); - std::for_each(begin, end, [&func, raw = std::get *>(pools)->begin(), this](const auto entity) mutable { - std::tuple, Other *...> pack; - std::get>(pack) = raw++; + std::for_each(begin, end, [raw = std::get *>(pools)->begin(), &func, this](const auto entity) mutable { + auto curr = raw++; - if(((std::get(pack) = std::get *>(pools)->try_get(entity)) && ...)) { + if((std::get *>(pools)->has(entity) && ...)) { if constexpr(std::is_invocable_v...>) { - func(*std::get, component_iterator_type, Component *>>(pack)...); + func(get(curr, std::get *>(pools), entity)...); } else { - func(entity, *std::get, component_iterator_type, Component *>>(pack)...); + func(entity, get(curr, std::get *>(pools), entity)...); } } }); diff --git a/src/entt/meta/factory.hpp b/src/entt/meta/factory.hpp index 808b24fca..bd82d3b5f 100644 --- a/src/entt/meta/factory.hpp +++ b/src/entt/meta/factory.hpp @@ -131,8 +131,7 @@ class meta_factory { template auto unregister_all(int) - -> decltype((internal::meta_info::type->*Member)->prop, void()) - { + -> decltype((internal::meta_info::type->*Member)->prop, void()) { while(internal::meta_info::type->*Member) { auto node = internal::meta_info::type->*Member; internal::meta_info::type->*Member = node->next; diff --git a/src/entt/meta/meta.hpp b/src/entt/meta/meta.hpp index 42ba1aac1..dbb3fc2ab 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -222,8 +222,7 @@ auto find_if(Op op, const Node *curr) ENTT_NOEXCEPT { template auto find_if(Op op, const meta_type_node *node) ENTT_NOEXCEPT --> decltype(find_if(op, node->*Member)) -{ +-> decltype(find_if(op, node->*Member)) { decltype(find_if(op, node->*Member)) ret = nullptr; if(node) { @@ -311,8 +310,7 @@ class meta_any { template inline static auto compare(int, const Type &lhs, const Type &rhs) - -> decltype(lhs == rhs, bool{}) - { + -> decltype(lhs == rhs, bool{}) { return lhs == rhs; } diff --git a/test/entt/entity/group.cpp b/test/entt/entity/group.cpp index c5a8c5222..45056395a 100644 --- a/test/entt/entity/group.cpp +++ b/test/entt/entity/group.cpp @@ -366,12 +366,11 @@ TEST(NonOwningGroup, EmptyAndNonEmptyTypes) { ASSERT_TRUE(entity == e0 || entity == e1); } - group.each([e0, e1](const auto entity, const int &, const empty_type &) { + group.each([e0, e1](const auto entity, const int &, empty_type) { ASSERT_TRUE(entity == e0 || entity == e1); }); ASSERT_EQ(group.size(), typename decltype(group)::size_type{2}); - ASSERT_EQ(&group.get(e0), &group.get(e1)); } TEST(NonOwningGroup, TrackEntitiesOnComponentDestruction) { @@ -872,12 +871,11 @@ TEST(OwningGroup, EmptyAndNonEmptyTypes) { ASSERT_TRUE(entity == e0 || entity == e1); } - group.each([e0, e1](const auto entity, const empty_type &, const int &) { + group.each([e0, e1](const auto entity, empty_type, const int &) { ASSERT_TRUE(entity == e0 || entity == e1); }); ASSERT_EQ(group.size(), typename decltype(group)::size_type{2}); - ASSERT_EQ(&group.get(e0), &group.get(e1)); } TEST(OwningGroup, TrackEntitiesOnComponentDestruction) { diff --git a/test/entt/entity/snapshot.cpp b/test/entt/entity/snapshot.cpp index 5e17fcc35..4c5c9465c 100644 --- a/test/entt/entity/snapshot.cpp +++ b/test/entt/entity/snapshot.cpp @@ -248,7 +248,6 @@ TEST(Snapshot, Continuous) { using storage_type = std::tuple< std::queue, - std::queue, std::queue, std::queue, std::queue @@ -432,10 +431,7 @@ TEST(Snapshot, MoreOnShrink) { entt::continuous_loader loader{dst}; - using storage_type = std::tuple< - std::queue, - std::queue - >; + using storage_type = std::tuple>; storage_type storage; output_archive output{storage}; diff --git a/test/entt/entity/storage.cpp b/test/entt/entity/storage.cpp index d388ccdd7..d2497c880 100644 --- a/test/entt/entity/storage.cpp +++ b/test/entt/entity/storage.cpp @@ -19,7 +19,7 @@ struct throwing_component { int data; }; -TEST(SparseSetWithType, Functionalities) { +TEST(Storage, Functionalities) { entt::storage set; set.reserve(42); @@ -79,16 +79,23 @@ TEST(SparseSetWithType, Functionalities) { other = std::move(set); } -TEST(SparseSetWithType, EmptyType) { +TEST(Storage, EmptyType) { entt::storage set; - ASSERT_EQ(&set.construct(42), &set.construct(99)); - ASSERT_EQ(&set.get(42), set.try_get(42)); - ASSERT_EQ(&set.get(42), &set.get(99)); + set.construct(42); + set.construct(99); + + ASSERT_TRUE(set.has(42)); + ASSERT_TRUE(set.has(99)); + ASSERT_EQ(set.try_get(42), nullptr); ASSERT_EQ(std::as_const(set).try_get(42), std::as_const(set).try_get(99)); + + auto &&component = set.get(42); + + ASSERT_TRUE((std::is_same_v)); } -TEST(SparseSetWithType, BatchAdd) { +TEST(Storage, BatchAdd) { entt::storage set; entt::storage::entity_type entities[2]; @@ -121,7 +128,7 @@ TEST(SparseSetWithType, BatchAdd) { ASSERT_EQ(set.get(entities[1]), 2); } -TEST(SparseSetWithType, BatchAddEmptyType) { +TEST(Storage, BatchAddEmptyType) { entt::storage set; entt::storage::entity_type entities[2]; @@ -130,7 +137,7 @@ TEST(SparseSetWithType, BatchAddEmptyType) { set.reserve(4); set.construct(12); - auto *component = set.batch(std::begin(entities), std::end(entities)); + set.batch(std::begin(entities), std::end(entities)); set.construct(24); ASSERT_TRUE(set.has(entities[0])); @@ -142,26 +149,26 @@ TEST(SparseSetWithType, BatchAddEmptyType) { ASSERT_FALSE(set.empty()); ASSERT_EQ(set.size(), 4u); - ASSERT_EQ(&set.get(entities[0]), &set.get(entities[1])); - ASSERT_EQ(&set.get(entities[0]), &set.get(12)); - ASSERT_EQ(&set.get(entities[0]), &set.get(24)); - ASSERT_EQ(&set.get(entities[0]), component); + + auto &&component = set.get(entities[0]); + + ASSERT_TRUE((std::is_same_v)); } -TEST(SparseSetWithType, AggregatesMustWork) { +TEST(Storage, AggregatesMustWork) { struct aggregate_type { int value; }; // the goal of this test is to enforce the requirements for aggregate types entt::storage{}.construct(0, 42); } -TEST(SparseSetWithType, TypesFromStandardTemplateLibraryMustWork) { +TEST(Storage, TypesFromStandardTemplateLibraryMustWork) { // see #37 - this test shouldn't crash, that's all entt::storage> set; set.construct(0).insert(42); set.destroy(0); } -TEST(SparseSetWithType, Iterator) { +TEST(Storage, Iterator) { using iterator_type = typename entt::storage::iterator_type; entt::storage set; @@ -203,7 +210,7 @@ TEST(SparseSetWithType, Iterator) { ASSERT_GE(end, set.end()); } -TEST(SparseSetWithType, ConstIterator) { +TEST(Storage, ConstIterator) { using iterator_type = typename entt::storage::const_iterator_type; entt::storage set; @@ -245,7 +252,7 @@ TEST(SparseSetWithType, ConstIterator) { ASSERT_GE(cend, set.cend()); } -TEST(SparseSetWithType, IteratorEmptyType) { +TEST(Storage, IteratorEmptyType) { using iterator_type = typename entt::storage::iterator_type; entt::storage set; set.construct(3); @@ -277,7 +284,7 @@ TEST(SparseSetWithType, IteratorEmptyType) { ASSERT_EQ(end - (end - begin), set.begin()); ASSERT_EQ(end + (begin - end), set.begin()); - ASSERT_EQ(&begin[0], set.begin().operator->()); + ASSERT_EQ(set.begin().operator->(), nullptr); ASSERT_LT(begin, end); ASSERT_LE(begin, set.begin()); @@ -286,60 +293,12 @@ TEST(SparseSetWithType, IteratorEmptyType) { ASSERT_GE(end, set.end()); set.construct(33); + auto &&component = *begin; - ASSERT_EQ(&*begin, &*(begin+1)); - ASSERT_EQ(&begin[0], &begin[1]); - ASSERT_EQ(begin.operator->(), (end-1).operator->()); + ASSERT_TRUE((std::is_same_v)); } -TEST(SparseSetWithType, ConstIteratorEmptyType) { - using iterator_type = typename entt::storage::const_iterator_type; - entt::storage set; - set.construct(3); - - iterator_type cend{set.cbegin()}; - iterator_type cbegin{}; - cbegin = set.cend(); - std::swap(cbegin, cend); - - ASSERT_EQ(cbegin, set.cbegin()); - ASSERT_EQ(cend, set.cend()); - ASSERT_NE(cbegin, cend); - - ASSERT_EQ(cbegin++, set.cbegin()); - ASSERT_EQ(cbegin--, set.cend()); - - ASSERT_EQ(cbegin+1, set.cend()); - ASSERT_EQ(cend-1, set.cbegin()); - - ASSERT_EQ(++cbegin, set.cend()); - ASSERT_EQ(--cbegin, set.cbegin()); - - ASSERT_EQ(cbegin += 1, set.cend()); - ASSERT_EQ(cbegin -= 1, set.cbegin()); - - ASSERT_EQ(cbegin + (cend - cbegin), set.cend()); - ASSERT_EQ(cbegin - (cbegin - cend), set.cend()); - - ASSERT_EQ(cend - (cend - cbegin), set.cbegin()); - ASSERT_EQ(cend + (cbegin - cend), set.cbegin()); - - ASSERT_EQ(&cbegin[0], set.cbegin().operator->()); - - ASSERT_LT(cbegin, cend); - ASSERT_LE(cbegin, set.cbegin()); - - ASSERT_GT(cend, cbegin); - ASSERT_GE(cend, set.cend()); - - set.construct(33); - - ASSERT_EQ(&*cbegin, &*(cbegin+1)); - ASSERT_EQ(&cbegin[0], &cbegin[1]); - ASSERT_EQ(cbegin.operator->(), (cend-1).operator->()); -} - -TEST(SparseSetWithType, Raw) { +TEST(Storage, Raw) { entt::storage set; set.construct(3, 3); @@ -355,7 +314,7 @@ TEST(SparseSetWithType, Raw) { ASSERT_EQ(*(set.raw() + 2u), 9); } -TEST(SparseSetWithType, RawEmptyType) { +TEST(Storage, RawEmptyType) { entt::storage set; set.construct(3); @@ -364,7 +323,7 @@ TEST(SparseSetWithType, RawEmptyType) { ASSERT_EQ(set.try_get(3), set.raw()); } -TEST(SparseSetWithType, SortOrdered) { +TEST(Storage, SortOrdered) { entt::storage set; set.construct(12, boxed_int{12}); @@ -400,7 +359,7 @@ TEST(SparseSetWithType, SortOrdered) { ASSERT_EQ(begin, end); } -TEST(SparseSetWithType, SortReverse) { +TEST(Storage, SortReverse) { entt::storage set; set.construct(12, boxed_int{1}); @@ -436,7 +395,7 @@ TEST(SparseSetWithType, SortReverse) { ASSERT_EQ(begin, end); } -TEST(SparseSetWithType, SortUnordered) { +TEST(Storage, SortUnordered) { entt::storage set; set.construct(12, boxed_int{6}); @@ -472,7 +431,7 @@ TEST(SparseSetWithType, SortUnordered) { ASSERT_EQ(begin, end); } -TEST(SparseSetWithType, RespectDisjoint) { +TEST(Storage, RespectDisjoint) { entt::storage lhs; entt::storage rhs; @@ -499,7 +458,7 @@ TEST(SparseSetWithType, RespectDisjoint) { ASSERT_EQ(begin, end); } -TEST(SparseSetWithType, RespectOverlap) { +TEST(Storage, RespectOverlap) { entt::storage lhs; entt::storage rhs; @@ -528,7 +487,7 @@ TEST(SparseSetWithType, RespectOverlap) { ASSERT_EQ(begin, end); } -TEST(SparseSetWithType, RespectOrdered) { +TEST(Storage, RespectOrdered) { entt::storage lhs; entt::storage rhs; @@ -574,7 +533,7 @@ TEST(SparseSetWithType, RespectOrdered) { ASSERT_EQ(*(rhs.data() + 5u), 5u); } -TEST(SparseSetWithType, RespectReverse) { +TEST(Storage, RespectReverse) { entt::storage lhs; entt::storage rhs; @@ -620,7 +579,7 @@ TEST(SparseSetWithType, RespectReverse) { ASSERT_EQ(*(rhs.data() + 5u), 5u); } -TEST(SparseSetWithType, RespectUnordered) { +TEST(Storage, RespectUnordered) { entt::storage lhs; entt::storage rhs; @@ -666,7 +625,7 @@ TEST(SparseSetWithType, RespectUnordered) { ASSERT_EQ(*(rhs.data() + 5u), 5u); } -TEST(SparseSetWithType, RespectOverlapEmptyType) { +TEST(Storage, RespectOverlapEmptyType) { entt::storage lhs; entt::storage rhs; @@ -687,7 +646,7 @@ TEST(SparseSetWithType, RespectOverlapEmptyType) { ASSERT_EQ(std::as_const(lhs).sparse_set::get(42), 1u); } -TEST(SparseSetWithType, CanModifyDuringIteration) { +TEST(Storage, CanModifyDuringIteration) { entt::storage set; set.construct(0, 42); @@ -703,7 +662,7 @@ TEST(SparseSetWithType, CanModifyDuringIteration) { (void)entity; } -TEST(SparseSetWithType, ReferencesGuaranteed) { +TEST(Storage, ReferencesGuaranteed) { entt::storage set; set.construct(0, 0); @@ -731,13 +690,13 @@ TEST(SparseSetWithType, ReferencesGuaranteed) { ASSERT_EQ(set.get(1).value, 3); } -TEST(SparseSetWithType, MoveOnlyComponent) { +TEST(Storage, MoveOnlyComponent) { // the purpose is to ensure that move only components are always accepted entt::storage> set; (void)set; } -TEST(SparseSetWithType, ConstructorExceptionDoesNotAddToSet) { +TEST(Storage, ConstructorExceptionDoesNotAddToSet) { entt::storage set; try { diff --git a/test/mod/mod.cpp b/test/mod/mod.cpp index eb1fbfdb4..fe10470ef 100644 --- a/test/mod/mod.cpp +++ b/test/mod/mod.cpp @@ -368,7 +368,7 @@ TEST(Mod, Duktape) { ASSERT_EQ(registry.view().size(), 3u); ASSERT_EQ(registry.view().size(), 2u); - registry.view().each([](auto, const duktape_runtime &runtime) { + registry.view().each([](const duktape_runtime &runtime) { ASSERT_EQ(runtime.components.size(), 2u); }); @@ -387,7 +387,7 @@ TEST(Mod, Duktape) { ASSERT_EQ(registry.view().size(), 3u); ASSERT_EQ(registry.view().size(), 2u); - registry.view().each([](auto, const position &position, const auto &...) { + registry.view().each([](const position &position, auto &&...) { ASSERT_EQ(position.x, -100.); ASSERT_EQ(position.y, -100.); });