storage/view/group:

* added extended_storage_iterator and storage<T...>::each function to return an extended iterable object
* use extended_storage_iterator and iterable_adaptor everywhere to avoid code duplication
This commit is contained in:
Michele Caini
2021-12-16 12:31:44 +01:00
parent 86d12cf317
commit c5cb1d9bcc
6 changed files with 404 additions and 294 deletions

View File

@@ -71,68 +71,46 @@ class basic_group<Entity, owned_t<>, get_t<Get...>, exclude_t<Exclude...>> final
using basic_common_type = std::common_type_t<typename storage_type<Get>::base_type...>;
class iterable final {
struct iterable_iterator final {
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
struct extended_group_iterator final {
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
template<typename... Args>
iterable_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Get> *...> &args) ENTT_NOEXCEPT
: it{from},
pools{args} {}
extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Get> *...> &args) ENTT_NOEXCEPT
: it{from},
pools{args} {}
iterable_iterator &operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
iterable_iterator operator++(int) ENTT_NOEXCEPT {
iterable_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
const auto entt = *it;
return std::tuple_cat(std::make_tuple(entt), std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
[[nodiscard]] bool operator==(const iterable_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const iterable_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
typename basic_common_type::iterator it;
std::tuple<storage_type<Get> *...> pools;
};
public:
using iterator = iterable_iterator;
iterable(basic_common_type *const ref, const std::tuple<storage_type<Get> *...> &cpools)
: handler{ref},
pools{cpools} {}
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return handler ? iterator{handler->begin(), pools} : iterator{{}, pools};
extended_group_iterator &operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return handler ? iterator{handler->end(), pools} : iterator{{}, pools};
extended_group_iterator operator++(int) ENTT_NOEXCEPT {
extended_group_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
const auto entt = *it;
return std::tuple_cat(std::make_tuple(entt), std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
basic_common_type *const handler;
const std::tuple<storage_type<Get> *...> pools;
typename basic_common_type::iterator it;
std::tuple<storage_type<Get> *...> pools;
};
basic_group(basic_common_type &ref, storage_type<Get> &...gpool) ENTT_NOEXCEPT
@@ -151,7 +129,7 @@ public:
/*! @brief Reversed iterator type. */
using reverse_iterator = typename base_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable_group = iterable;
using iterable = iterable_adaptor<extended_group_iterator>;
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() ENTT_NOEXCEPT
@@ -401,8 +379,9 @@ public:
*
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable_group each() const ENTT_NOEXCEPT {
return iterable_group{handler, pools};
[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
return handler ? iterable{extended_group_iterator{handler->begin(), pools}, extended_group_iterator{handler->end(), pools}}
: iterable{extended_group_iterator{{}, pools}, extended_group_iterator{{}, pools}};
}
/**
@@ -545,78 +524,55 @@ class basic_group<Entity, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...
using basic_common_type = std::common_type_t<typename storage_type<Owned>::base_type..., typename storage_type<Get>::base_type...>;
class iterable final {
template<typename>
struct iterable_iterator;
template<typename>
struct extended_group_iterator;
template<typename... OIt>
struct iterable_iterator<type_list<OIt...>> final {
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
template<typename... OIt>
struct extended_group_iterator<type_list<OIt...>> final {
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
template<typename... Other>
iterable_iterator(typename basic_common_type::iterator from, const std::tuple<Other...> &other, const std::tuple<storage_type<Get> *...> &cpools) ENTT_NOEXCEPT
: it{from},
owned{std::get<OIt>(other)...},
get{cpools} {}
template<typename... Other>
extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<Other...> &other, const std::tuple<storage_type<Get> *...> &cpools) ENTT_NOEXCEPT
: it{from},
owned{std::get<OIt>(other)...},
get{cpools} {}
iterable_iterator &operator++() ENTT_NOEXCEPT {
return ++it, (++std::get<OIt>(owned), ...), *this;
}
iterable_iterator operator++(int) ENTT_NOEXCEPT {
iterable_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return std::tuple_cat(
std::make_tuple(*it),
std::forward_as_tuple(*std::get<OIt>(owned)...),
std::get<storage_type<Get> *>(get)->get_as_tuple(*it)...);
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
[[nodiscard]] bool operator==(const iterable_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const iterable_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
typename basic_common_type::iterator it;
std::tuple<OIt...> owned;
std::tuple<storage_type<Get> *...> get;
};
public:
using iterator = iterable_iterator<type_list_cat_t<std::conditional_t<ignore_as_empty_v<std::remove_const_t<Owned>>, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().end())>>...>>;
iterable(std::tuple<storage_type<Owned> *..., storage_type<Get> *...> cpools, const std::size_t *const extent)
: pools{cpools},
length{extent} {}
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
auto it = length ? (std::get<0>(pools)->basic_common_type::end() - *length) : typename basic_common_type::iterator{};
return iterator{std::move(it), std::make_tuple((std::get<storage_type<Owned> *>(pools)->end() - *length)...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
extended_group_iterator &operator++() ENTT_NOEXCEPT {
return ++it, (++std::get<OIt>(owned), ...), *this;
}
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
auto it = length ? std::get<0>(pools)->basic_common_type::end() : typename basic_common_type::iterator{};
return iterator{std::move(it), std::make_tuple((std::get<storage_type<Owned> *>(pools)->end())...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
extended_group_iterator operator++(int) ENTT_NOEXCEPT {
extended_group_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return std::tuple_cat(
std::make_tuple(*it),
std::forward_as_tuple(*std::get<OIt>(owned)...),
std::get<storage_type<Get> *>(get)->get_as_tuple(*it)...);
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools;
const std::size_t *const length;
typename basic_common_type::iterator it;
std::tuple<OIt...> owned;
std::tuple<storage_type<Get> *...> get;
};
basic_group(const std::size_t &extent, storage_type<Owned> &...opool, storage_type<Get> &...gpool) ENTT_NOEXCEPT
@@ -635,7 +591,7 @@ public:
/*! @brief Reversed iterator type. */
using reverse_iterator = typename base_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable_group = iterable;
using iterable = iterable_adaptor<extended_group_iterator<type_list_cat_t<std::conditional_t<ignore_as_empty_v<std::remove_const_t<Owned>>, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().end())>>...>>>;
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() ENTT_NOEXCEPT
@@ -861,8 +817,12 @@ public:
*
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable_group each() const ENTT_NOEXCEPT {
return iterable_group{pools, length};
[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
using extended_iterator_type = typename iterable::iterator;
iterator last = length ? std::get<0>(pools)->basic_common_type::end() : iterator{};
auto from = extended_iterator_type{last - *length, std::make_tuple((std::get<storage_type<Owned> *>(pools)->end() - *length)...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
auto to = extended_iterator_type{last, std::make_tuple((std::get<storage_type<Owned> *>(pools)->end())...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
return {std::move(from), std::move(to)};
}
/**

View File

@@ -12,6 +12,7 @@
#include "../core/algorithm.hpp"
#include "../core/any.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/iterator.hpp"
#include "../core/memory.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
@@ -155,6 +156,63 @@ template<typename CLhs, typename CRhs>
return !(lhs < rhs);
}
template<typename It, typename... Other>
class extended_storage_iterator final {
template<typename Iter, typename... Args>
friend class extended_storage_iterator;
public:
using iterator_type = It;
using difference_type = typename std::iterator_traits<It>::difference_type;
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::forward_as_tuple(*std::declval<Other>()...)));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
extended_storage_iterator() ENTT_NOEXCEPT = default;
extended_storage_iterator(It base, Other... other) ENTT_NOEXCEPT
: it{base, other...} {}
template<typename... Args, typename = std::enable_if_t<(!std::is_same_v<Other, Args> && ...) && (std::is_constructible_v<Other, Args> && ...)>>
extended_storage_iterator(const extended_storage_iterator<It, Args...> &other) ENTT_NOEXCEPT
: it{other.it} {}
extended_storage_iterator &operator++() ENTT_NOEXCEPT {
return ++std::get<It>(it), (++std::get<Other>(it), ...), *this;
}
extended_storage_iterator operator++(int) ENTT_NOEXCEPT {
extended_storage_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return {*std::get<It>(it), *std::get<Other>(it)...};
}
[[nodiscard]] iterator_type base() const ENTT_NOEXCEPT {
return std::get<It>(it);
}
private:
std::tuple<It, Other...> it;
};
template<typename... CLhs, typename... CRhs>
[[nodiscard]] bool operator==(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT {
return lhs.base() == rhs.base();
}
template<typename... CLhs, typename... CRhs>
[[nodiscard]] bool operator!=(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
} // namespace internal
/**
@@ -165,15 +223,6 @@ template<typename CLhs, typename CRhs>
/**
* @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 extend sparse sets to store
* components in a registry. It guarantees fast access both to the elements and
* to the entities.
*
* @note
* Entities and objects have the same order.
*
* @note
* Internal data structures arrange elements to maximize performance. There are
* no guarantees that objects are returned in the insertion order when iterate
* a storage. Do not make assumption on the order in any case.
@@ -182,8 +231,6 @@ template<typename CLhs, typename CRhs>
* Empty types aren't explicitly instantiated. Therefore, many of the functions
* normally available for non-empty types will not be available for empty ones.
*
* @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.
* @tparam Allocator Type of allocator used to manage memory and elements.
@@ -379,6 +426,10 @@ public:
using reverse_iterator = std::reverse_iterator<iterator>;
/*! @brief Constant reverse iterator type. */
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
/*! @brief Extended iterable storage proxy. */
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator, iterator>>;
/*! @brief Constant extended iterable storage proxy. */
using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator, const_iterator>>;
/*! @brief Default constructor. */
basic_storage()
@@ -705,6 +756,23 @@ public:
consume_range(std::move(first), std::move(last), [&from]() -> decltype(auto) { return *(from++); });
}
/**
* @brief Returns an iterable object to use to _visit_ the storage.
*
* The iterable object returns a tuple that contains the current entity and
* a reference to its component.
*
* @return An iterable object to use to _visit_ the storage.
*/
[[nodiscard]] iterable each() ENTT_NOEXCEPT {
return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}};
}
/*! @copydoc each */
[[nodiscard]] const_iterable each() const ENTT_NOEXCEPT {
return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}};
}
private:
compressed_pair<container_type, alloc> packed;
};
@@ -727,6 +795,10 @@ public:
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Extended iterable storage proxy. */
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
/*! @brief Constant extended iterable storage proxy. */
using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>;
/*! @brief Default constructor. */
basic_storage()
@@ -844,6 +916,22 @@ public:
emplace(*first);
}
}
/**
* @brief Returns an iterable object to use to _visit_ the storage.
*
* The iterable object returns a tuple that contains the current entity.
*
* @return An iterable object to use to _visit_ the storage.
*/
[[nodiscard]] iterable each() ENTT_NOEXCEPT {
return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}};
}
/*! @copydoc each */
[[nodiscard]] const_iterable each() const ENTT_NOEXCEPT {
return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}};
}
};
/**

View File

@@ -26,133 +26,6 @@ namespace entt {
namespace internal {
template<typename Storage>
class iterable_storage final {
using base_type = typename Storage::base_type;
template<typename... It>
struct iterable_storage_iterator final {
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::tuple<typename Storage::entity_type>{}, std::declval<decltype(std::declval<Storage>().get_as_tuple({}))>()));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
template<typename... Discard>
iterable_storage_iterator(It... from, Discard...) ENTT_NOEXCEPT
: it{from...} {}
iterable_storage_iterator &operator++() ENTT_NOEXCEPT {
return (++std::get<It>(it), ...), *this;
}
iterable_storage_iterator operator++(int) ENTT_NOEXCEPT {
iterable_storage_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return {*std::get<It>(it)...};
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
[[nodiscard]] bool operator==(const iterable_storage_iterator &other) const ENTT_NOEXCEPT {
return std::get<0>(other.it) == std::get<0>(it);
}
[[nodiscard]] bool operator!=(const iterable_storage_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
std::tuple<It...> it;
};
public:
using iterator = std::conditional_t<
ignore_as_empty_v<typename Storage::value_type>,
iterable_storage_iterator<typename base_type::iterator>,
iterable_storage_iterator<typename base_type::iterator, decltype(std::declval<Storage>().begin())>>;
iterable_storage(Storage &ref)
: pool{&ref} {}
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return iterator{pool->base_type::begin(), pool->begin()};
}
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return iterator{pool->base_type::end(), pool->end()};
}
private:
Storage *const pool;
};
template<typename View>
class iterable_view final {
struct iterable_view_iterator final {
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::tuple<typename View::entity_type>{}, std::declval<View>().get({})));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
iterable_view_iterator(typename View::iterator from, const View *parent) ENTT_NOEXCEPT
: it{from},
view{parent} {}
iterable_view_iterator &operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
iterable_view_iterator operator++(int) ENTT_NOEXCEPT {
iterable_view_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return std::tuple_cat(std::make_tuple(*it), view->get(*it));
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
[[nodiscard]] bool operator==(const iterable_view_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const iterable_view_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
typename View::iterator it;
const View *view;
};
public:
using iterator = iterable_view_iterator;
iterable_view(const View &parent)
: view{parent} {}
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return {view.begin(), &view};
}
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return {view.end(), &view};
}
private:
const View view;
};
template<typename Type, std::size_t Component, std::size_t Exclude>
class view_iterator final {
[[nodiscard]] bool valid() const {
@@ -195,14 +68,6 @@ public:
return ++(*this), orig;
}
[[nodiscard]] bool operator==(const view_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const view_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
[[nodiscard]] pointer operator->() const {
return &*it;
}
@@ -211,6 +76,10 @@ public:
return *operator->();
}
[[nodiscard]] iterator_type base() const ENTT_NOEXCEPT {
return it;
}
private:
iterator_type it;
iterator_type last;
@@ -218,6 +87,65 @@ private:
std::array<const Type *, Exclude> filter;
};
template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
[[nodiscard]] bool operator==(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT {
return lhs.base() == rhs.base();
}
template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
[[nodiscard]] bool operator!=(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
template<typename It, typename... Storage>
struct extended_view_iterator final {
using iterator_type = It;
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Storage>().get_as_tuple({})...));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
extended_view_iterator(It from, std::tuple<Storage *...> storage) ENTT_NOEXCEPT
: it{from},
pools{storage} {}
extended_view_iterator &operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
extended_view_iterator operator++(int) ENTT_NOEXCEPT {
extended_view_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools);
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
[[nodiscard]] iterator_type base() const ENTT_NOEXCEPT {
return it;
}
private:
It it;
std::tuple<Storage *...> pools;
};
template<typename... Lhs, typename... Rhs>
[[nodiscard]] bool operator==(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT {
return lhs.base() == rhs.base();
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] bool operator!=(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
} // namespace internal
/**
@@ -285,7 +213,7 @@ class basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>> {
template<std::size_t Comp, typename Func, std::size_t... Index>
void each(Func func, std::index_sequence<Index...>) const {
for(const auto curr: internal::iterable_storage{*std::get<Comp>(pools)}) {
for(const auto curr: std::get<Comp>(pools)->each()) {
const auto entt = std::get<0>(curr);
if(((sizeof...(Component) != 1u) || (entt != tombstone))
@@ -315,7 +243,7 @@ public:
/*! @brief Bidirectional iterator type. */
using iterator = internal::view_iterator<base_type, sizeof...(Component) - 1u, sizeof...(Exclude)>;
/*! @brief Iterable view type. */
using iterable_view = internal::iterable_view<basic_view>;
using iterable = iterable_adaptor<internal::extended_view_iterator<iterator, storage_type<Component>...>>;
/*! @brief Default constructor to use to create empty, invalid views. */
basic_view() ENTT_NOEXCEPT
@@ -549,14 +477,14 @@ public:
/**
* @brief Returns an iterable object to use to _visit_ the view.
*
* The iterable object returns tuples that contain the current entity and a
* set of references to its non-empty components. The _constness_ of the
* The iterable object returns a tuple that contains the current entity and
* a set of references to its non-empty components. The _constness_ of the
* components is as requested.
*
* @return An iterable object to use to _visit_ the view.
*/
[[nodiscard]] iterable_view each() const ENTT_NOEXCEPT {
return iterable_view{*this};
[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}};
}
/**
@@ -623,7 +551,7 @@ public:
/*! @brief Reversed iterator type. */
using reverse_iterator = typename base_type::reverse_iterator;
/*! @brief Iterable view type. */
using iterable_view = internal::iterable_storage<storage_type>;
using iterable = decltype(std::declval<storage_type>().each());
/*! @brief Default constructor to use to create empty, invalid views. */
basic_view() ENTT_NOEXCEPT
@@ -874,14 +802,14 @@ public:
/**
* @brief Returns an iterable object to use to _visit_ the view.
*
* The iterable object returns tuples that contain the current entity and a
* reference to its component if it's a non-empty one. The _constness_ of
* The iterable object returns a tuple that contains the current entity and
* a reference to its component if it's a non-empty one. The _constness_ of
* the component is as requested.
*
* @return An iterable object to use to _visit_ the view.
*/
[[nodiscard]] iterable_view each() const ENTT_NOEXCEPT {
return iterable_view{*std::get<0>(pools)};
[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
return std::get<0>(pools)->each();
}
/**

View File

@@ -183,15 +183,15 @@ TEST(NonOwningGroup, Each) {
auto group = registry.group(entt::get<int, char>);
auto cgroup = std::as_const(registry).group_if_exists(entt::get<const int, const char>);
auto iterable = group.each();
auto citerable = cgroup.each();
registry.emplace<int>(entity[0u], 0);
registry.emplace<char>(entity[0u], 0);
registry.emplace<int>(entity[1u], 1);
registry.emplace<char>(entity[1u], 1);
auto iterable = group.each();
auto citerable = cgroup.each();
ASSERT_NE(citerable.begin(), citerable.end());
ASSERT_NO_THROW(iterable.begin()->operator=(*iterable.begin()));
@@ -826,15 +826,15 @@ TEST(OwningGroup, Each) {
auto group = registry.group<int>(entt::get<char>);
auto cgroup = std::as_const(registry).group_if_exists<const int>(entt::get<const char>);
auto iterable = group.each();
auto citerable = cgroup.each();
registry.emplace<int>(entity[0u], 0);
registry.emplace<char>(entity[0u], 0);
registry.emplace<int>(entity[1u], 1);
registry.emplace<char>(entity[1u], 1);
auto iterable = group.each();
auto citerable = cgroup.each();
ASSERT_NE(citerable.begin(), citerable.end());
ASSERT_NO_THROW(iterable.begin()->operator=(*iterable.begin()));

View File

@@ -1037,6 +1037,140 @@ TEST(Storage, IteratorConversion) {
ASSERT_NE(++cit, it);
}
TEST(Storage, Iterable) {
using iterator = typename entt::storage<boxed_int>::iterable::iterator;
static_assert(std::is_same_v<iterator::value_type, std::tuple<entt::entity, boxed_int &>>);
static_assert(std::is_same_v<iterator::value_type, std::tuple<entt::entity, boxed_int &>>);
static_assert(std::is_same_v<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity, boxed_int &>>>);
static_assert(std::is_same_v<typename iterator::reference, typename iterator::value_type>);
entt::storage<boxed_int> pool;
pool.emplace(entt::entity{1}, 99);
pool.emplace(entt::entity{3}, 42);
auto iterable = pool.each();
iterator end{iterable.begin()};
iterator begin{};
begin = iterable.end();
std::swap(begin, end);
ASSERT_EQ(begin, iterable.begin());
ASSERT_EQ(end, iterable.end());
ASSERT_NE(begin, end);
ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{3});
ASSERT_EQ(std::get<1>(*begin.operator->().operator->()), boxed_int{42});
ASSERT_EQ(std::get<0>(*begin), entt::entity{3});
ASSERT_EQ(std::get<1>(*begin), boxed_int{42});
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(++begin, iterable.end());
for(auto [entity, element]: iterable) {
static_assert(std::is_same_v<decltype(entity), entt::entity>);
static_assert(std::is_same_v<decltype(element), boxed_int &>);
ASSERT_TRUE(entity != entt::entity{1} || element == boxed_int{99});
ASSERT_TRUE(entity != entt::entity{3} || element == boxed_int{42});
}
}
TEST(Storage, ConstIterable) {
using iterator = typename entt::storage<boxed_int>::const_iterable::const_iterator;
static_assert(std::is_same_v<iterator::value_type, std::tuple<entt::entity, const boxed_int &>>);
static_assert(std::is_same_v<iterator::value_type, std::tuple<entt::entity, const boxed_int &>>);
static_assert(std::is_same_v<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity, const boxed_int &>>>);
static_assert(std::is_same_v<typename iterator::reference, typename iterator::value_type>);
entt::storage<boxed_int> pool;
pool.emplace(entt::entity{1}, 99);
pool.emplace(entt::entity{3}, 42);
auto iterable = std::as_const(pool).each();
iterator end{iterable.cbegin()};
iterator begin{};
begin = iterable.cend();
std::swap(begin, end);
ASSERT_EQ(begin, iterable.cbegin());
ASSERT_EQ(end, iterable.cend());
ASSERT_NE(begin, end);
ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{3});
ASSERT_EQ(std::get<1>(*begin.operator->().operator->()), boxed_int{42});
ASSERT_EQ(std::get<0>(*begin), entt::entity{3});
ASSERT_EQ(std::get<1>(*begin), boxed_int{42});
ASSERT_EQ(begin++, iterable.cbegin());
ASSERT_EQ(++begin, iterable.cend());
for(auto [entity, element]: iterable) {
static_assert(std::is_same_v<decltype(entity), entt::entity>);
static_assert(std::is_same_v<decltype(element), const boxed_int &>);
ASSERT_TRUE(entity != entt::entity{1} || element == boxed_int{99});
ASSERT_TRUE(entity != entt::entity{3} || element == boxed_int{42});
}
}
TEST(Storage, IterableIteratorConversion) {
entt::storage<boxed_int> pool;
pool.emplace(entt::entity{3}, 42);
typename entt::storage<boxed_int>::iterable::iterator it = pool.each().begin();
typename entt::storage<boxed_int>::const_iterable::const_iterator cit = it;
static_assert(std::is_same_v<decltype(*it), std::tuple<entt::entity, boxed_int &>>);
static_assert(std::is_same_v<decltype(*cit), std::tuple<entt::entity, const boxed_int &>>);
ASSERT_EQ(it, cit);
ASSERT_NE(++cit, it);
}
TEST(Storage, EmptyTypeIterable) {
using iterator = typename entt::storage<empty_stable_type>::iterable::iterator;
static_assert(std::is_same_v<iterator::value_type, std::tuple<entt::entity>>);
static_assert(std::is_same_v<iterator::value_type, std::tuple<entt::entity>>);
static_assert(std::is_same_v<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity>>>);
static_assert(std::is_same_v<typename iterator::reference, typename iterator::value_type>);
entt::storage<empty_stable_type> pool;
pool.emplace(entt::entity{1});
pool.emplace(entt::entity{3});
auto iterable = pool.each();
iterator end{iterable.begin()};
iterator begin{};
begin = iterable.end();
std::swap(begin, end);
ASSERT_EQ(begin, iterable.begin());
ASSERT_EQ(end, iterable.end());
ASSERT_NE(begin, end);
ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{3});
ASSERT_EQ(std::get<0>(*begin), entt::entity{3});
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(++begin, iterable.end());
for(auto [entity]: iterable) {
static_assert(std::is_same_v<decltype(entity), entt::entity>);
ASSERT_TRUE(entity == entt::entity{1} || entity == entt::entity{3});
}
}
TEST(Storage, IterableAlgorithmCompatibility) {
entt::storage<boxed_int> pool;
pool.emplace(entt::entity{3}, 42);
const auto iterable = pool.each();
const auto it = std::find_if(iterable.begin(), iterable.end(), [](auto args) { return std::get<0>(args) == entt::entity{3}; });
ASSERT_EQ(std::get<0>(*it), entt::entity{3});
}
TEST(Storage, Raw) {
entt::storage<int> pool;

View File

@@ -176,12 +176,12 @@ TEST(SingleComponentView, Each) {
auto view = registry.view<int>();
auto cview = std::as_const(registry).view<const int>();
auto iterable = view.each();
auto citerable = cview.each();
registry.emplace<int>(entity[0u], 0);
registry.emplace<int>(entity[1u], 1);
auto iterable = view.each();
auto citerable = cview.each();
ASSERT_NE(citerable.begin(), citerable.end());
ASSERT_NO_THROW(iterable.begin()->operator=(*iterable.begin()));
@@ -677,15 +677,15 @@ TEST(MultiComponentView, Each) {
auto view = registry.view<int, char>();
auto cview = std::as_const(registry).view<const int, const char>();
auto iterable = view.each();
auto citerable = cview.each();
registry.emplace<int>(entity[0u], 0);
registry.emplace<char>(entity[0u], 0);
registry.emplace<int>(entity[1u], 1);
registry.emplace<char>(entity[1u], 1);
auto iterable = view.each();
auto citerable = cview.each();
ASSERT_NE(citerable.begin(), citerable.end());
ASSERT_NO_THROW(iterable.begin()->operator=(*iterable.begin()));