registry (close #521):
* remove visit * added storage_proxy_iterator (input iterator category, modeled as a random access iterator) * added ::storage() (const and non-const) to return an iterable object over all pools
This commit is contained in:
@@ -1208,11 +1208,11 @@ them by copy if needed:
|
||||
|
||||
```cpp
|
||||
// create a copy of an entity component by component
|
||||
registry.visit([entity, other](auto &&storage) {
|
||||
if(storage.contains(entity)) {
|
||||
for(auto &&curr: registry.storage()) {
|
||||
if(auto &storage = curr.second; storage.contains(entity)) {
|
||||
storage.emplace(other, storage.get(entity));
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
This is particularly useful to clone entities in an opaque way. In addition, the
|
||||
|
||||
@@ -266,17 +266,26 @@ struct basic_handle {
|
||||
|
||||
/**
|
||||
* @brief Visits a handle and returns the pools for its components.
|
||||
* @sa basic_registry::visit
|
||||
*
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(id_type, const basic_sparse_set<entity_type> &);
|
||||
* @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<typename Func>
|
||||
void visit(Func &&func) const {
|
||||
reg->visit([func = std::forward<Func>(func), this](auto &&storage) {
|
||||
for(auto [id, storage]: reg->storage()) {
|
||||
if(storage.contains(entt)) {
|
||||
func(storage);
|
||||
func(id, storage);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "../core/algorithm.hpp"
|
||||
#include "../core/any.hpp"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../core/utility.hpp"
|
||||
@@ -29,6 +30,141 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename It>
|
||||
class storage_proxy_iterator {
|
||||
template<typename Other>
|
||||
friend class storage_proxy_iterator;
|
||||
|
||||
using iterator_traits = std::iterator_traits<It>;
|
||||
using first_type = typename iterator_traits::value_type::first_type;
|
||||
using second_type = typename iterator_traits::value_type::second_type::element_type;
|
||||
|
||||
public:
|
||||
using value_type = std::pair<first_type, constness_as_t<second_type, std::remove_reference_t<typename iterator_traits::reference>> &>;
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using difference_type = typename iterator_traits::difference_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
storage_proxy_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
storage_proxy_iterator(const It iter) ENTT_NOEXCEPT
|
||||
: it{iter} {}
|
||||
|
||||
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
|
||||
storage_proxy_iterator(const storage_proxy_iterator<Other> &other)
|
||||
: it{other.it} {}
|
||||
|
||||
storage_proxy_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return ++it, *this;
|
||||
}
|
||||
|
||||
storage_proxy_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
storage_proxy_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
storage_proxy_iterator &operator--() ENTT_NOEXCEPT {
|
||||
return --it, *this;
|
||||
}
|
||||
|
||||
storage_proxy_iterator operator--(int) ENTT_NOEXCEPT {
|
||||
storage_proxy_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
storage_proxy_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
it += value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
storage_proxy_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
storage_proxy_iterator copy = *this;
|
||||
return (copy += value);
|
||||
}
|
||||
|
||||
storage_proxy_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
storage_proxy_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator[](const difference_type value) const {
|
||||
return {it[value].first, *it[value].second};
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
return {it->first, *it->second};
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
return operator*();
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend auto operator-(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend bool operator==(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend bool operator<(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
|
||||
|
||||
private:
|
||||
It it;
|
||||
};
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] auto operator-(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.it - rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator==(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.it == rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator!=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator<(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.it < rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator>(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator<=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator>=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Fast and reliable entity-component system.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
@@ -181,6 +317,23 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ a registry.
|
||||
*
|
||||
* The iterable object returns a pair that contains the name and a reference
|
||||
* to the current storage.
|
||||
*
|
||||
* @return An iterable object to use to _visit_ the registry.
|
||||
*/
|
||||
[[nodiscard]] auto storage() ENTT_NOEXCEPT {
|
||||
return iterable_adaptor{internal::storage_proxy_iterator{pools.begin()}, internal::storage_proxy_iterator{pools.end()}};
|
||||
}
|
||||
|
||||
/*! @copydoc storage */
|
||||
[[nodiscard]] auto storage() const ENTT_NOEXCEPT {
|
||||
return iterable_adaptor{internal::storage_proxy_iterator{pools.cbegin()}, internal::storage_proxy_iterator{pools.cend()}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type.
|
||||
* @tparam Component Type of component of which to return the storage.
|
||||
@@ -583,7 +736,7 @@ public:
|
||||
/**
|
||||
* @brief Patches the given component for an entity.
|
||||
*
|
||||
* The signature of the functions should be equivalent to the following:
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(Component &);
|
||||
@@ -1266,27 +1419,6 @@ public:
|
||||
assure<To>().respect(assure<From>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Visits a registry and returns the pools for its components.
|
||||
*
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const basic_sparse_set<entity_type> &);
|
||||
* @endcode
|
||||
*
|
||||
* Returned pools are those of the components managed by the registry.
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void visit(Func func) const {
|
||||
for(auto &&curr: pools) {
|
||||
func(*curr.second);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Binds an object to the context of the registry.
|
||||
*
|
||||
|
||||
@@ -169,7 +169,10 @@ TEST(BasicHandle, Component) {
|
||||
ASSERT_TRUE(registry.storage<double>().empty());
|
||||
ASSERT_EQ(0u, (handle.remove<char, double>()));
|
||||
|
||||
handle.visit([](const auto &pool) { ASSERT_EQ(entt::type_id<int>(), pool.type()); });
|
||||
handle.visit([&handle](const auto id, const auto &pool) {
|
||||
ASSERT_EQ(id, entt::type_id<int>().hash());
|
||||
ASSERT_TRUE(pool.contains(handle.entity()));
|
||||
});
|
||||
|
||||
ASSERT_TRUE((handle.any_of<int, char, double>()));
|
||||
ASSERT_FALSE((handle.all_of<int, char, double>()));
|
||||
|
||||
@@ -1854,26 +1854,6 @@ TEST(Registry, AssignEntities) {
|
||||
ASSERT_EQ(traits_type::to_entity(other.create()), traits_type::to_integral(entities[1]));
|
||||
}
|
||||
|
||||
TEST(Registry, Visit) {
|
||||
entt::registry registry;
|
||||
const auto entity = registry.create();
|
||||
const auto other = registry.create();
|
||||
|
||||
registry.emplace<int>(entity);
|
||||
registry.emplace<double>(other);
|
||||
registry.emplace<char>(entity);
|
||||
|
||||
bool hasType[3]{};
|
||||
|
||||
registry.visit([&hasType](const auto &pool) {
|
||||
hasType[0] = hasType[0] || (pool.type() == entt::type_id<int>());
|
||||
hasType[1] = hasType[1] || (pool.type() == entt::type_id<double>());
|
||||
hasType[2] = hasType[2] || (pool.type() == entt::type_id<char>());
|
||||
});
|
||||
|
||||
ASSERT_TRUE(hasType[0] && hasType[1] && hasType[2]);
|
||||
}
|
||||
|
||||
TEST(Registry, ScramblingPoolsIsAllowed) {
|
||||
entt::registry registry;
|
||||
registry.on_destroy<int>().connect<&listener::sort<int>>();
|
||||
@@ -1926,3 +1906,85 @@ TEST(Registry, RuntimePools) {
|
||||
ASSERT_FALSE(storage.contains(entity));
|
||||
ASSERT_FALSE(registry.any_of<empty_type>(entity));
|
||||
}
|
||||
|
||||
TEST(Registry, StorageProxy) {
|
||||
using namespace entt::literals;
|
||||
|
||||
entt::registry registry;
|
||||
const auto entity = registry.create();
|
||||
auto &storage = registry.storage<int>("int"_hs);
|
||||
storage.emplace(entity);
|
||||
|
||||
for(auto [id, pool]: registry.storage()) {
|
||||
static_assert(std::is_same_v<decltype(pool), entt::sparse_set &>);
|
||||
static_assert(std::is_same_v<decltype(id), const entt::id_type>);
|
||||
|
||||
ASSERT_TRUE(pool.contains(entity));
|
||||
ASSERT_EQ(std::addressof(storage), std::addressof(pool));
|
||||
ASSERT_EQ(id, "int"_hs);
|
||||
}
|
||||
|
||||
for(auto &&curr: std::as_const(registry).storage()) {
|
||||
static_assert(std::is_same_v<decltype(curr.second), const entt::sparse_set &>);
|
||||
static_assert(std::is_same_v<decltype(curr.first), const entt::id_type>);
|
||||
|
||||
ASSERT_TRUE(curr.second.contains(entity));
|
||||
ASSERT_EQ(std::addressof(storage), std::addressof(curr.second));
|
||||
ASSERT_EQ(curr.first, "int"_hs);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Registry, StorageProxyIterator) {
|
||||
entt::registry registry;
|
||||
const auto entity = registry.create();
|
||||
registry.emplace<int>(entity);
|
||||
|
||||
auto test = [entity](auto iterable) {
|
||||
auto end{iterable.begin()};
|
||||
decltype(end) begin{};
|
||||
begin = iterable.end();
|
||||
std::swap(begin, end);
|
||||
|
||||
ASSERT_EQ(begin, iterable.cbegin());
|
||||
ASSERT_EQ(end, iterable.cend());
|
||||
ASSERT_NE(begin, end);
|
||||
|
||||
ASSERT_EQ(begin++, iterable.begin());
|
||||
ASSERT_EQ(begin--, iterable.end());
|
||||
|
||||
ASSERT_EQ(begin + 1, iterable.end());
|
||||
ASSERT_EQ(end - 1, iterable.begin());
|
||||
|
||||
ASSERT_EQ(++begin, iterable.end());
|
||||
ASSERT_EQ(--begin, iterable.begin());
|
||||
|
||||
ASSERT_EQ(begin += 1, iterable.end());
|
||||
ASSERT_EQ(begin -= 1, iterable.begin());
|
||||
|
||||
ASSERT_EQ(begin + (end - begin), iterable.end());
|
||||
ASSERT_EQ(begin - (begin - end), iterable.end());
|
||||
|
||||
ASSERT_EQ(end - (end - begin), iterable.begin());
|
||||
ASSERT_EQ(end + (begin - end), iterable.begin());
|
||||
|
||||
ASSERT_EQ(begin[0u].first, iterable.begin()->first);
|
||||
ASSERT_EQ(std::addressof(begin[0u].second), std::addressof((*iterable.begin()).second));
|
||||
|
||||
ASSERT_LT(begin, end);
|
||||
ASSERT_LE(begin, iterable.begin());
|
||||
|
||||
ASSERT_GT(end, begin);
|
||||
ASSERT_GE(end, iterable.end());
|
||||
|
||||
ASSERT_EQ(begin[0u].first, entt::type_id<int>().hash());
|
||||
ASSERT_TRUE(begin[0u].second.contains(entity));
|
||||
};
|
||||
|
||||
test(registry.storage());
|
||||
test(std::as_const(registry).storage());
|
||||
|
||||
decltype(std::as_const(registry).storage().begin()) cit = registry.storage().begin();
|
||||
|
||||
ASSERT_EQ(cit, registry.storage().begin());
|
||||
ASSERT_NE(cit, std::as_const(registry).storage().end());
|
||||
}
|
||||
|
||||
@@ -22,17 +22,17 @@ TEST(Example, EntityCopy) {
|
||||
ASSERT_FALSE((registry.any_of<int, char, double>(dst)));
|
||||
ASSERT_FALSE(custom.contains(dst));
|
||||
|
||||
registry.visit([src, dst](auto &&storage) {
|
||||
// discard chars because why not, this is just an example after all
|
||||
if(storage.type() != entt::type_id<char>() && storage.contains(src)) {
|
||||
for(auto [id, storage]: registry.storage()) {
|
||||
// discard the custom storage because why not, this is just an example after all
|
||||
if(id != "custom"_hs && storage.contains(src)) {
|
||||
storage.emplace(dst, storage.get(src));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ASSERT_TRUE((registry.all_of<int>(dst)));
|
||||
ASSERT_FALSE((registry.any_of<char, double>(dst)));
|
||||
ASSERT_TRUE(custom.contains(dst));
|
||||
ASSERT_TRUE((registry.all_of<int, char>(dst)));
|
||||
ASSERT_FALSE((registry.all_of<double>(dst)));
|
||||
ASSERT_FALSE(custom.contains(dst));
|
||||
|
||||
ASSERT_EQ(registry.get<int>(dst), 42);
|
||||
ASSERT_EQ(custom.get(dst), 1.);
|
||||
ASSERT_EQ(registry.get<char>(dst), 'c');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user