diff --git a/TODO b/TODO index 6ffd47884..e52889287 100644 --- a/TODO +++ b/TODO @@ -13,9 +13,9 @@ DOC: * update entity doc when the storage based model is in place WIP: +* dense_map/set and registry: move ctor/op should be noexcept (op is conditionally noexcept though) * "replace" organizer with flow * add directed/undirected support to adjacency matrix (see brief) and refine iterators a little -* handle: replace visit with an iterable object * add storage getter for filters to views and groups * exploit the tombstone mechanism to allow enabling/disabling entities (see bump, compact and clear for further details) * emitter: runtime handlers, allocator support (ready for both already) diff --git a/src/entt/entity/handle.hpp b/src/entt/entity/handle.hpp index 345392047..09534474c 100644 --- a/src/entt/entity/handle.hpp +++ b/src/entt/entity/handle.hpp @@ -1,6 +1,7 @@ #ifndef ENTT_ENTITY_HANDLE_HPP #define ENTT_ENTITY_HANDLE_HPP +#include #include #include #include @@ -10,6 +11,88 @@ namespace entt { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +class handle_storage_iterator final { + template + friend class handle_storage_iterator; + + using underlying_type = std::remove_reference_t; + using entity_type = typename underlying_type::entity_type; + +public: + using value_type = typename std::iterator_traits::value_type; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + + constexpr handle_storage_iterator() noexcept + : entt{null}, + it{}, + last{} {} + + constexpr handle_storage_iterator(entity_type value, It from, It to) noexcept + : entt{value}, + it{from}, + last{to} { + while(it != last && !it->second.contains(entt)) { ++it; } + } + + template && std::is_constructible_v>> + constexpr handle_storage_iterator(const handle_storage_iterator &other) noexcept + : handle_storage_iterator{other.entt, other it, other.last} {} + + constexpr handle_storage_iterator &operator++() noexcept { + while(++it != last && !it->second.contains(entt)) {} + return *this; + } + + constexpr handle_storage_iterator operator++(int) noexcept { + handle_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *it; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + template + friend constexpr bool operator==(const handle_storage_iterator &, const handle_storage_iterator &) noexcept; + +private: + entity_type entt; + It it; + It last; +}; + +template +[[nodiscard]] constexpr bool operator==(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + /** * @brief Non-owning handle to an entity. * @@ -43,6 +126,29 @@ struct basic_handle { : reg{&ref}, entt{value} {} + /** + * @brief Returns an iterable object to use to _visit_ a handle. + * + * The iterable object returns a pair that contains the name and a reference + * to the current storage.
+ * Returned storage are those that contain the entity associated with the + * handle. + * + * @return An iterable object to use to _visit_ the handle. + */ + [[nodiscard]] auto storage() noexcept { + auto iterable = reg->storage(); + using iterator_type = internal::handle_storage_iterator; + return iterable_adaptor{iterator_type{entt, iterable.begin(), iterable.end()}, iterator_type{entt, iterable.end(), iterable.end()}}; + } + + /*! @copydoc storage */ + [[nodiscard]] auto storage() const noexcept { + auto iterable = reg->storage(); + using iterator_type = internal::handle_storage_iterator; + return iterable_adaptor{iterator_type{entt, iterable.cbegin(), iterable.cend()}, iterator_type{entt, iterable.cend(), iterable.cend()}}; + } + /** * @brief Constructs a const handle from a non-const one. * @tparam Other A valid entity type (see entt_traits for more details). @@ -263,30 +369,6 @@ struct basic_handle { return reg->orphan(entt); } - /** - * @brief Visits a handle and returns the pools for its components. - * - * The signature of the function should be equivalent to the following: - * - * @code{.cpp} - * void(id_type, const basic_sparse_set &); - * @endcode - * - * Returned pools are those that contain the entity associated with the - * handle. - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void visit(Func &&func) const { - for(auto [id, storage]: reg->storage()) { - if(storage.contains(entt)) { - func(id, storage); - } - } - } - private: registry_type *reg; entity_type entt; diff --git a/test/entt/entity/handle.cpp b/test/entt/entity/handle.cpp index 40efaeb09..018c0a900 100644 --- a/test/entt/entity/handle.cpp +++ b/test/entt/entity/handle.cpp @@ -170,10 +170,10 @@ TEST(BasicHandle, Component) { ASSERT_TRUE(registry.storage().empty()); ASSERT_EQ(0u, (handle.remove())); - handle.visit([&handle](const auto id, const auto &pool) { + for(auto [id, pool]: handle.storage()) { ASSERT_EQ(id, entt::type_id().hash()); ASSERT_TRUE(pool.contains(handle.entity())); - }); + } ASSERT_TRUE((handle.any_of())); ASSERT_FALSE((handle.all_of()));