* split view iterator from multi-type view
* stable/packed storage policy support
This commit is contained in:
Michele Caini
2021-06-21 13:04:37 +02:00
parent 216871fe56
commit 2c35203647
3 changed files with 381 additions and 188 deletions

View File

@@ -22,7 +22,7 @@ class basic_registry;
template<typename...>
class basic_view;
struct basic_view;
template<typename>

View File

@@ -10,6 +10,7 @@
#include <type_traits>
#include "../config/config.h"
#include "../core/type_traits.hpp"
#include "component.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "sparse_set.hpp"
@@ -21,13 +22,153 @@ namespace entt {
/**
* @brief View.
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Policy, typename It, std::size_t AllOf, std::size_t NoneOf>
class view_iterator final {
using basic_common_type = basic_sparse_set<typename std::iterator_traits<It>::value_type>;
[[nodiscard]] bool valid() const {
const auto entt = *it;
return Policy::accept(entt)
&& std::apply([entt](const auto *... curr) { return (curr->contains(entt) && ...); }, pools)
&& std::apply([entt](const auto *... curr) { return (!curr->contains(entt) && ...); }, filter);
}
public:
using difference_type = typename std::iterator_traits<It>::difference_type;
using value_type = typename std::iterator_traits<It>::value_type;
using pointer = typename std::iterator_traits<It>::pointer;
using reference = typename std::iterator_traits<It>::reference;
using iterator_category = std::bidirectional_iterator_tag;
view_iterator() ENTT_NOEXCEPT
: first{},
last{},
it{},
pools{},
filter{}
{}
view_iterator(It from, It to, It curr, std::array<const basic_common_type *, AllOf> all_of, std::array<const basic_common_type *, NoneOf> none_of) ENTT_NOEXCEPT
: first{from},
last{to},
it{curr},
pools{all_of},
filter{none_of}
{
if(it != last && !valid()) {
++(*this);
}
}
view_iterator & operator++() ENTT_NOEXCEPT {
while(++it != last && !valid());
return *this;
}
view_iterator operator++(int) ENTT_NOEXCEPT {
view_iterator orig = *this;
return ++(*this), orig;
}
view_iterator & operator--() ENTT_NOEXCEPT {
while(--it != first && !valid());
return *this;
}
view_iterator operator--(int) ENTT_NOEXCEPT {
view_iterator orig = *this;
return operator--(), 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;
}
[[nodiscard]] reference operator*() const {
return *operator->();
}
private:
It first;
It last;
It it;
std::array<const basic_common_type *, AllOf> pools;
std::array<const basic_common_type *, NoneOf> filter;
};
}
/*! @brief Stable storage policy, aimed at pointer stability. */
struct stable_storage_policy {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
template<typename Entity>
[[nodiscard]] static constexpr bool accept(const Entity entity) ENTT_NOEXCEPT {
return entity != tombstone;
}
/**
* Internal details not to be documented.
* @endcond
*/
};
/*! @brief Packed storage policy, aimed at faster linear iteration. */
struct packed_storage_policy {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
template<typename Entity>
[[nodiscard]] static constexpr bool accept(const Entity) ENTT_NOEXCEPT {
return true;
}
/**
* Internal details not to be documented.
* @endcond
*/
};
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief View implementation.
*
* Primary template isn't defined on purpose. All the specializations give a
* compile-time error, but for a few reasonable cases.
*/
template<typename...>
class basic_view;
class basic_view_impl;
/*! @brief View implementation dispatcher. */
template<typename...>
struct basic_view;
/**
@@ -59,120 +200,38 @@ class basic_view;
* Lifetime of a view must not overcome that of the registry that generated it.
* In any other case, attempting to use a view results in undefined behavior.
*
* @tparam Policy Common (stricter) storage policy.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Exclude Types of components used to filter the view.
* @tparam Component Types of components iterated by the view.
*/
template<typename Entity, typename... Exclude, typename... Component>
class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
template<typename Policy, typename Entity, typename... Exclude, typename... Component>
class basic_view_impl<Policy, Entity, exclude_t<Exclude...>, Component...> {
using basic_common_type = basic_sparse_set<Entity>;
template<typename Comp>
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
using unchecked_type = std::array<const basic_sparse_set<Entity> *, (sizeof...(Component) - 1)>;
template<typename It>
class view_iterator final {
friend class basic_view<Entity, exclude_t<Exclude...>, Component...>;
view_iterator(It from, It to, It curr, unchecked_type other, const std::tuple<const storage_type<Exclude> *...> &ignore) ENTT_NOEXCEPT
: first{from},
last{to},
it{curr},
unchecked{other},
filter{ignore}
{
if(it != last && !valid()) {
++(*this);
}
}
[[nodiscard]] bool valid() const {
const auto entt = *it;
return std::all_of(unchecked.cbegin(), unchecked.cend(), [entt](const basic_sparse_set<Entity> *curr) { return curr->contains(entt); })
&& !(std::get<const storage_type<Exclude> *>(filter)->contains(entt) || ...);
}
public:
using difference_type = typename std::iterator_traits<It>::difference_type;
using value_type = typename std::iterator_traits<It>::value_type;
using pointer = typename std::iterator_traits<It>::pointer;
using reference = typename std::iterator_traits<It>::reference;
using iterator_category = std::bidirectional_iterator_tag;
view_iterator() ENTT_NOEXCEPT
: view_iterator{{}, {}, {}, {}, {}}
{}
view_iterator & operator++() ENTT_NOEXCEPT {
while(++it != last && !valid());
return *this;
}
view_iterator operator++(int) ENTT_NOEXCEPT {
view_iterator orig = *this;
return ++(*this), orig;
}
view_iterator & operator--() ENTT_NOEXCEPT {
while(--it != first && !valid());
return *this;
}
view_iterator operator--(int) ENTT_NOEXCEPT {
view_iterator orig = *this;
return operator--(), 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;
}
[[nodiscard]] reference operator*() const {
return *operator->();
}
private:
It first;
It last;
It it;
unchecked_type unchecked;
std::tuple<const storage_type<Exclude> *...> filter;
};
class iterable_view final {
friend class basic_view<Entity, exclude_t<Exclude...>, Component...>;
class iterable final {
template<typename It>
class iterable_view_iterator final {
friend class iterable_view;
iterable_view_iterator(It from, const basic_view *parent) ENTT_NOEXCEPT
: it{from},
view{parent}
{}
public:
struct iterable_iterator final {
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_view>().get({})));
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_view_impl>().get({})));
using pointer = void;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
iterable_view_iterator & operator++() ENTT_NOEXCEPT {
iterable_iterator(It from, const basic_view_impl *parent) ENTT_NOEXCEPT
: it{from},
view{parent}
{}
iterable_iterator & operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
iterable_view_iterator operator++(int) ENTT_NOEXCEPT {
iterable_view_iterator orig = *this;
iterable_iterator operator++(int) ENTT_NOEXCEPT {
iterable_iterator orig = *this;
return ++(*this), orig;
}
@@ -180,27 +239,27 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
return std::tuple_cat(std::make_tuple(*it), view->get(*it));
}
[[nodiscard]] bool operator==(const iterable_view_iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator==(const iterable_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const iterable_view_iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator!=(const iterable_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
It it;
const basic_view *view;
const basic_view_impl *view;
};
iterable_view(const basic_view &parent)
public:
using iterator = iterable_iterator<internal::view_iterator<Policy, typename basic_common_type::iterator, sizeof...(Component) - 1u, sizeof...(Exclude)>>;
using reverse_iterator = iterable_iterator<internal::view_iterator<Policy, typename basic_common_type::reverse_iterator, sizeof...(Component) - 1u, sizeof...(Exclude)>>;
iterable(const basic_view_impl &parent)
: view{parent}
{}
public:
using iterator = iterable_view_iterator<view_iterator<typename basic_sparse_set<Entity>::iterator>>;
using reverse_iterator = iterable_view_iterator<view_iterator<typename basic_sparse_set<Entity>::reverse_iterator>>;
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return { view.begin(), &view };
}
@@ -218,22 +277,26 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
}
private:
const basic_view view;
const basic_view_impl view;
};
[[nodiscard]] const basic_sparse_set<Entity> * candidate() const ENTT_NOEXCEPT {
return (std::min)({ static_cast<const basic_sparse_set<entity_type> *>(std::get<storage_type<Component> *>(pools))... }, [](const auto *lhs, const auto *rhs) {
[[nodiscard]] const auto * candidate() const ENTT_NOEXCEPT {
return (std::min)({ static_cast<const basic_common_type *>(std::get<storage_type<Component> *>(pools))... }, [](const auto *lhs, const auto *rhs) {
return lhs->size() < rhs->size();
});
}
[[nodiscard]] unchecked_type unchecked(const basic_sparse_set<Entity> *cpool) const {
[[nodiscard]] auto pools_to_unchecked_array() const ENTT_NOEXCEPT {
std::size_t pos{};
unchecked_type other{};
(static_cast<void>(std::get<storage_type<Component> *>(pools) == cpool ? void() : void(other[pos++] = std::get<storage_type<Component> *>(pools))), ...);
std::array<const basic_common_type *, sizeof...(Component) - 1u> other{};
(static_cast<void>(std::get<storage_type<Component> *>(pools) == view ? void() : void(other[pos++] = std::get<storage_type<Component> *>(pools))), ...);
return other;
}
[[nodiscard]] auto filter_to_array() const ENTT_NOEXCEPT {
return std::array<const basic_common_type *, sizeof...(Exclude)>{std::get<const storage_type<Exclude> *>(filter)...};
}
template<typename Comp, typename It>
[[nodiscard]] auto dispatch_get([[maybe_unused]] It &it, [[maybe_unused]] const Entity entt) const {
if constexpr(std::is_same_v<typename std::iterator_traits<It>::value_type, typename storage_type<Comp>::value_type>) {
@@ -246,11 +309,11 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
template<typename Comp, typename Func>
void traverse(Func func) const {
if constexpr(std::is_void_v<decltype(std::get<storage_type<Comp> *>(pools)->get({}))>) {
for(const auto entt: static_cast<const basic_sparse_set<entity_type> &>(*std::get<storage_type<Comp> *>(pools))) {
if(((std::is_same_v<Comp, Component> || std::get<storage_type<Component> *>(pools)->contains(entt)) && ...)
&& !(std::get<const storage_type<Exclude> *>(filter)->contains(entt) || ...))
for(const auto entt: static_cast<const basic_common_type &>(*std::get<storage_type<Comp> *>(pools))) {
if(Policy::accept(entt) && ((std::is_same_v<Comp, Component> || std::get<storage_type<Component> *>(pools)->contains(entt)) && ...)
&& (!std::get<const storage_type<Exclude> *>(filter)->contains(entt) && ...))
{
if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) {
if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view_impl>().get({})))>) {
std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt)));
} else {
std::apply(func, get(entt));
@@ -260,11 +323,11 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
} else {
auto it = std::get<storage_type<Comp> *>(pools)->begin();
for(const auto entt: static_cast<const basic_sparse_set<entity_type> &>(*std::get<storage_type<Comp> *>(pools))) {
if(((std::is_same_v<Comp, Component> || std::get<storage_type<Component> *>(pools)->contains(entt)) && ...)
&& !(std::get<const storage_type<Exclude> *>(filter)->contains(entt) || ...))
for(const auto entt: static_cast<const basic_common_type &>(*std::get<storage_type<Comp> *>(pools))) {
if(Policy::accept(entt) && ((std::is_same_v<Comp, Component> || std::get<storage_type<Component> *>(pools)->contains(entt)) && ...)
&& (!std::get<const storage_type<Exclude> *>(filter)->contains(entt) && ...))
{
if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) {
if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view_impl>().get({})))>) {
std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get<Component>(it, entt)...));
} else {
std::apply(func, std::tuple_cat(dispatch_get<Component>(it, entt)...));
@@ -282,12 +345,14 @@ public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Bidirectional iterator type. */
using iterator = view_iterator<typename basic_sparse_set<entity_type>::iterator>;
using iterator = internal::view_iterator<Policy, typename basic_common_type::iterator, sizeof...(Component) - 1u, sizeof...(Exclude)>;
/*! @brief Reverse iterator type. */
using reverse_iterator = view_iterator<typename basic_sparse_set<entity_type>::reverse_iterator>;
using reverse_iterator = internal::view_iterator<Policy, typename basic_common_type::reverse_iterator, sizeof...(Component) - 1u, sizeof...(Exclude)>;
/*! @brief Iterable view type. */
using iterable_view = iterable;
/*! @brief Default constructor to use to create empty, invalid views. */
basic_view() ENTT_NOEXCEPT
basic_view_impl() ENTT_NOEXCEPT
: view{}
{}
@@ -296,7 +361,7 @@ public:
* @param component The storage for the types to iterate.
* @param epool The storage for the types used to filter the view.
*/
basic_view(storage_type<Component> &... component, const storage_type<Exclude> &... epool) ENTT_NOEXCEPT
basic_view_impl(storage_type<Component> &... component, const storage_type<Exclude> &... epool) ENTT_NOEXCEPT
: pools{&component...},
filter{&epool...},
view{candidate()}
@@ -328,7 +393,7 @@ public:
* @return An iterator to the first entity of the view.
*/
[[nodiscard]] iterator begin() const {
return iterator{view->begin(), view->end(), view->begin(), unchecked(view), filter};
return iterator{view->begin(), view->end(), view->begin(), pools_to_unchecked_array(), filter_to_array()};
}
/**
@@ -341,7 +406,7 @@ public:
* @return An iterator to the entity following the last entity of the view.
*/
[[nodiscard]] iterator end() const {
return iterator{view->begin(), view->end(), view->end(), unchecked(view), filter};
return iterator{view->begin(), view->end(), view->end(), pools_to_unchecked_array(), filter_to_array()};
}
/**
@@ -353,7 +418,7 @@ public:
* @return An iterator to the first entity of the reversed view.
*/
[[nodiscard]] reverse_iterator rbegin() const {
return reverse_iterator{view->rbegin(), view->rend(), view->rbegin(), unchecked(view), filter};
return reverse_iterator{view->rbegin(), view->rend(), view->rbegin(), pools_to_unchecked_array(), filter_to_array()};
}
/**
@@ -368,7 +433,7 @@ public:
* reversed view.
*/
[[nodiscard]] reverse_iterator rend() const {
return reverse_iterator{view->rbegin(), view->rend(), view->rend(), unchecked(view), filter};
return reverse_iterator{view->rbegin(), view->rend(), view->rend(), pools_to_unchecked_array(), filter_to_array()};
}
/**
@@ -398,7 +463,7 @@ public:
* iterator otherwise.
*/
[[nodiscard]] iterator find(const entity_type entt) const {
const auto it = iterator{view->begin(), view->end(), view->find(entt), unchecked(view), filter};
const auto it = iterator{view->begin(), view->end(), view->find(entt), pools_to_unchecked_array(), filter_to_array()};
return (it != end() && *it == entt) ? it : end();
}
@@ -416,7 +481,7 @@ public:
* @return True if the view contains the given entity, false otherwise.
*/
[[nodiscard]] bool contains(const entity_type entt) const {
return (std::get<storage_type<Component> *>(pools)->contains(entt) && ...) && !(std::get<const storage_type<Exclude> *>(filter)->contains(entt) || ...);
return (std::get<storage_type<Component> *>(pools)->contains(entt) && ...) && (!std::get<const storage_type<Exclude> *>(filter)->contains(entt) && ...);
}
/**
@@ -480,9 +545,7 @@ public:
*
* The pool of the suggested component is used to lead the iterations. The
* returned entities will therefore respect the order of the pool associated
* with that type.<br/>
* It is no longer guaranteed that the performance is the best possible, but
* there will be greater control over the order of iteration.
* with that type.
*
* @sa each
*
@@ -518,9 +581,7 @@ public:
*
* The pool of the suggested component is used to lead the iterations. The
* returned elements will therefore respect the order of the pool associated
* with that type.<br/>
* It is no longer guaranteed that the performance is the best possible, but
* there will be greater control over the order of iteration.
* with that type.
*
* @sa each
*
@@ -548,7 +609,7 @@ public:
private:
const std::tuple<storage_type<Component> *...> pools;
const std::tuple<const storage_type<Exclude> *...> filter;
mutable const basic_sparse_set<entity_type> *view;
mutable const basic_common_type *view;
};
@@ -584,34 +645,30 @@ private:
* @tparam Component Type of component iterated by the view.
*/
template<typename Entity, typename Component>
class basic_view<Entity, exclude_t<>, Component> final {
class basic_view_impl<packed_storage_policy, Entity, exclude_t<>, Component> {
using basic_common_type = basic_sparse_set<Entity>;
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>;
class iterable_view final {
friend class basic_view<Entity, exclude_t<>, Component>;
class iterable final {
template<typename... It>
class iterable_view_iterator final {
friend class iterable_view;
template<typename... Discard>
iterable_view_iterator(It... from, Discard...) ENTT_NOEXCEPT
: it{from...}
{}
public:
struct iterable_iterator final {
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_view>().get({})));
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_view_impl>().get({})));
using pointer = void;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
iterable_view_iterator & operator++() ENTT_NOEXCEPT {
template<typename... Discard>
iterable_iterator(It... from, Discard...) ENTT_NOEXCEPT
: it{from...}
{}
iterable_iterator & operator++() ENTT_NOEXCEPT {
return (++std::get<It>(it), ...), *this;
}
iterable_view_iterator operator++(int) ENTT_NOEXCEPT {
iterable_view_iterator orig = *this;
iterable_iterator operator++(int) ENTT_NOEXCEPT {
iterable_iterator orig = *this;
return ++(*this), orig;
}
@@ -619,11 +676,11 @@ class basic_view<Entity, exclude_t<>, Component> final {
return { *std::get<It>(it)... };
}
[[nodiscard]] bool operator==(const iterable_view_iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator==(const iterable_iterator &other) const ENTT_NOEXCEPT {
return std::get<0>(other.it) == std::get<0>(it);
}
[[nodiscard]] bool operator!=(const iterable_view_iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator!=(const iterable_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
@@ -631,36 +688,36 @@ class basic_view<Entity, exclude_t<>, Component> final {
std::tuple<It...> it;
};
iterable_view(storage_type &ref)
: pool{&ref}
{}
public:
using iterator = std::conditional_t<
std::is_void_v<decltype(std::declval<storage_type>().get({}))>,
iterable_view_iterator<typename basic_sparse_set<Entity>::iterator>,
iterable_view_iterator<typename basic_sparse_set<Entity>::iterator, decltype(std::declval<storage_type>().begin())>
iterable_iterator<typename basic_common_type::iterator>,
iterable_iterator<typename basic_common_type::iterator, decltype(std::declval<storage_type>().begin())>
>;
using reverse_iterator = std::conditional_t<
std::is_void_v<decltype(std::declval<storage_type>().get({}))>,
iterable_view_iterator<typename basic_sparse_set<Entity>::reverse_iterator>,
iterable_view_iterator<typename basic_sparse_set<Entity>::reverse_iterator, decltype(std::declval<storage_type>().rbegin())>
iterable_iterator<typename basic_common_type::reverse_iterator>,
iterable_iterator<typename basic_common_type::reverse_iterator, decltype(std::declval<storage_type>().rbegin())>
>;
iterable(storage_type &ref)
: pool{&ref}
{}
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return iterator{pool->basic_sparse_set<entity_type>::begin(), pool->begin()};
return iterator{pool->basic_common_type::begin(), pool->begin()};
}
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return iterator{pool->basic_sparse_set<entity_type>::end(), pool->end()};
return iterator{pool->basic_common_type::end(), pool->end()};
}
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
return reverse_iterator{pool->basic_sparse_set<entity_type>::rbegin(), pool->rbegin()};
return reverse_iterator{pool->basic_common_type::rbegin(), pool->rbegin()};
}
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
return reverse_iterator{pool->basic_sparse_set<entity_type>::rend(), pool->rend()};
return reverse_iterator{pool->basic_common_type::rend(), pool->rend()};
}
private:
@@ -673,12 +730,14 @@ public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Random access iterator type. */
using iterator = typename basic_sparse_set<Entity>::iterator;
using iterator = typename basic_common_type::iterator;
/*! @brief Reversed iterator type. */
using reverse_iterator = typename basic_sparse_set<Entity>::reverse_iterator;
using reverse_iterator = typename basic_common_type::reverse_iterator;
/*! @brief Iterable view type. */
using iterable_view = iterable;
/*! @brief Default constructor to use to create empty, invalid views. */
basic_view() ENTT_NOEXCEPT
basic_view_impl() ENTT_NOEXCEPT
: pools{},
filter{}
{}
@@ -687,7 +746,7 @@ public:
* @brief Constructs a single-type view from a storage class.
* @param ref The storage for the type to iterate.
*/
basic_view(storage_type &ref) ENTT_NOEXCEPT
basic_view_impl(storage_type &ref) ENTT_NOEXCEPT
: pools{&ref},
filter{}
{}
@@ -737,7 +796,7 @@ public:
* @return An iterator to the first entity of the view.
*/
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return std::get<0>(pools)->basic_sparse_set<entity_type>::begin();
return std::get<0>(pools)->basic_common_type::begin();
}
/**
@@ -750,7 +809,7 @@ public:
* @return An iterator to the entity following the last entity of the view.
*/
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return std::get<0>(pools)->basic_sparse_set<entity_type>::end();
return std::get<0>(pools)->basic_common_type::end();
}
/**
@@ -762,7 +821,7 @@ public:
* @return An iterator to the first entity of the reversed view.
*/
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
return std::get<0>(pools)->basic_sparse_set<entity_type>::rbegin();
return std::get<0>(pools)->basic_common_type::rbegin();
}
/**
@@ -777,7 +836,7 @@ public:
* reversed view.
*/
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
return std::get<0>(pools)->basic_sparse_set<entity_type>::rend();
return std::get<0>(pools)->basic_common_type::rend();
}
/**
@@ -946,6 +1005,22 @@ private:
};
/**
* @brief View implementation dispatcher.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Exclude Types of components used to filter the view.
* @tparam Component Types of components iterated by the view.
*/
template<typename Entity, typename... Exclude, typename... Component>
struct basic_view<Entity, exclude_t<Exclude...>, Component...>
: basic_view_impl<std::conditional_t<std::disjunction_v<typename component_traits<std::remove_const_t<Component>>::in_place_delete...>, stable_storage_policy, packed_storage_policy>, Entity, exclude_t<Exclude...>, Component...>
{
/*! @brief Most restrictive storage policy of all component types. */
using storage_policy = std::conditional_t<std::disjunction_v<typename component_traits<std::remove_const_t<Component>>::in_place_delete...>, stable_storage_policy, packed_storage_policy>;
using basic_view_impl<storage_policy, Entity, exclude_t<Exclude...>, Component...>::basic_view_impl;
};
/**
* @brief Deduction guide.
* @tparam Storage Type of storage classes used to create the view.

View File

@@ -7,6 +7,13 @@
#include <entt/entity/view.hpp>
struct empty_type {};
struct stable_type { int value; };
template<>
struct entt::component_traits<stable_type> {
using in_place_delete = std::true_type;
using ignore_if_empty = std::true_type;
};
TEST(SingleComponentView, Functionalities) {
entt::registry registry;
@@ -378,10 +385,16 @@ TEST(SingleComponentView, FrontBack) {
TEST(SingleComponentView, DeductionGuide) {
entt::registry registry;
typename entt::storage_traits<entt::entity, int>::storage_type storage;
typename entt::storage_traits<entt::entity, int>::storage_type istorage;
typename entt::storage_traits<entt::entity, stable_type>::storage_type sstorage;
static_assert(std::is_same_v<decltype(entt::basic_view{storage}), entt::basic_view<entt::entity, entt::exclude_t<>, int>>);
static_assert(std::is_same_v<decltype(entt::basic_view{std::as_const(storage)}), entt::basic_view<entt::entity, entt::exclude_t<>, const int>>);
static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, int>, decltype(entt::basic_view{istorage})>);
static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, const int>, decltype(entt::basic_view{std::as_const(istorage)})>);
static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, stable_type>, decltype(entt::basic_view{sstorage})>);
static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<>, int>, decltype(entt::basic_view{istorage})>);
static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<>, const int>, decltype(entt::basic_view{std::as_const(istorage)})>);
static_assert(std::is_base_of_v<entt::basic_view_impl<entt::stable_storage_policy, entt::entity, entt::exclude_t<>, stable_type>, decltype(entt::basic_view{sstorage})>);
}
TEST(SingleComponentView, IterableViewAlgorithmCompatibility) {
@@ -397,6 +410,47 @@ TEST(SingleComponentView, IterableViewAlgorithmCompatibility) {
ASSERT_EQ(std::get<0>(*it), entity);
}
TEST(SingleComponentView, StableType) {
entt::registry registry;
auto view = registry.view<stable_type>();
const auto entity = registry.create();
const auto other = registry.create();
registry.emplace<stable_type>(entity);
registry.emplace<stable_type>(other);
registry.destroy(entity);
ASSERT_EQ(view.size_hint(), 2u);
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(view.contains(other));
ASSERT_EQ(view.front(), other);
ASSERT_EQ(view.back(), other);
ASSERT_EQ(*view.begin(), other);
ASSERT_EQ(++view.begin(), view.end());
view.each([other](const auto entt, stable_type) {
ASSERT_EQ(other, entt);
});
view.each([check = true](stable_type) mutable {
ASSERT_TRUE(check);
check = false;
});
for(auto [entt, st]: view.each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(st), stable_type &>);
ASSERT_EQ(other, entt);
}
registry.compact();
ASSERT_EQ(view.size_hint(), 1u);
}
TEST(MultiComponentView, Functionalities) {
entt::registry registry;
auto view = registry.view<int, char>();
@@ -937,11 +991,19 @@ TEST(MultiComponentView, DeductionGuide) {
entt::registry registry;
typename entt::storage_traits<entt::entity, int>::storage_type istorage;
typename entt::storage_traits<entt::entity, double>::storage_type dstorage;
typename entt::storage_traits<entt::entity, stable_type>::storage_type sstorage;
static_assert(std::is_same_v<decltype(entt::basic_view{istorage, dstorage}), entt::basic_view<entt::entity, entt::exclude_t<>, int, double>>);
static_assert(std::is_same_v<decltype(entt::basic_view{std::as_const(istorage), dstorage}), entt::basic_view<entt::entity, entt::exclude_t<>, const int, double>>);
static_assert(std::is_same_v<decltype(entt::basic_view{istorage, std::as_const(dstorage)}), entt::basic_view<entt::entity, entt::exclude_t<>, int, const double>>);
static_assert(std::is_same_v<decltype(entt::basic_view{std::as_const(istorage), std::as_const(dstorage)}), entt::basic_view<entt::entity, entt::exclude_t<>, const int, const double>>);
static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, int, double>, decltype(entt::basic_view{istorage, dstorage})>);
static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, const int, double>, decltype(entt::basic_view{std::as_const(istorage), dstorage})>);
static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, int, const double>, decltype(entt::basic_view{istorage, std::as_const(dstorage)})>);
static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, const int, const double>, decltype(entt::basic_view{std::as_const(istorage), std::as_const(dstorage)})>);
static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, int, stable_type>, decltype(entt::basic_view{istorage, sstorage})>);
static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<>, int, double>, decltype(entt::basic_view{istorage, dstorage})>);
static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<>, const int, double>, decltype(entt::basic_view{std::as_const(istorage), dstorage})>);
static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<>, int, const double>, decltype(entt::basic_view{istorage, std::as_const(dstorage)})>);
static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<>, const int, const double>, decltype(entt::basic_view{std::as_const(istorage), std::as_const(dstorage)})>);
static_assert(std::is_base_of_v<entt::basic_view_impl<entt::stable_storage_policy, entt::entity, entt::exclude_t<>, int, stable_type>, decltype(entt::basic_view{istorage, sstorage})>);
}
TEST(MultiComponentView, IterableViewAlgorithmCompatibility) {
@@ -958,6 +1020,54 @@ TEST(MultiComponentView, IterableViewAlgorithmCompatibility) {
ASSERT_EQ(std::get<0>(*it), entity);
}
TEST(MultiComponentView, StableType) {
entt::registry registry;
auto view = registry.view<int, stable_type>();
const auto entity = registry.create();
const auto other = registry.create();
registry.emplace<int>(entity);
registry.emplace<int>(other);
registry.emplace<stable_type>(entity);
registry.emplace<stable_type>(other);
registry.destroy(entity);
ASSERT_EQ(view.size_hint(), 1u);
view.use<stable_type>();
ASSERT_EQ(view.size_hint(), 2u);
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(view.contains(other));
ASSERT_EQ(view.front(), other);
ASSERT_EQ(view.back(), other);
ASSERT_EQ(*view.begin(), other);
ASSERT_EQ(++view.begin(), view.end());
view.each([other](const auto entt, int, stable_type) {
ASSERT_EQ(other, entt);
});
view.each([check = true](int, stable_type) mutable {
ASSERT_TRUE(check);
check = false;
});
for(auto [entt, iv, st]: view.each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
static_assert(std::is_same_v<decltype(st), stable_type &>);
ASSERT_EQ(other, entt);
}
registry.compact();
ASSERT_EQ(view.size_hint(), 1u);
}
TEST(View, Pipe) {
entt::registry registry;
const auto entity = registry.create();
@@ -970,15 +1080,20 @@ TEST(View, Pipe) {
registry.emplace<int>(other);
registry.emplace<char>(other);
registry.emplace<stable_type>(other);
const auto view1 = registry.view<int>(entt::exclude<double>);
const auto view2 = registry.view<const char>(entt::exclude<float>);
const auto view3 = registry.view<empty_type>();
const auto view4 = registry.view<stable_type>();
static_assert(std::is_same_v<decltype(view1 | view2), entt::basic_view<entt::entity, entt::exclude_t<double, float>, int, const char>>);
static_assert(std::is_same_v<decltype(view2 | view1), entt::basic_view<entt::entity, entt::exclude_t<float, double>, const char, int>>);
static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<double, float>, int, const char>, decltype(view1 | view2)>);
static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<float, double>, const char, int>, decltype(view2 | view1)>);
static_assert(std::is_same_v<decltype((view1 | view2) | view3), decltype(view1 | (view2 | view3))>);
static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<double, float>, int, const char>, decltype(view1 | view2)>);
static_assert(std::is_base_of_v<entt::basic_view_impl<entt::stable_storage_policy, entt::entity, entt::exclude_t<double, float>, int, stable_type, const char>, decltype(view1 | view4 | view2)>);
ASSERT_FALSE((view1 | view2).contains(entity));
ASSERT_TRUE((view1 | view2).contains(other));
@@ -987,4 +1102,7 @@ TEST(View, Pipe) {
ASSERT_FALSE((view1 | view2 | view3).contains(entity));
ASSERT_FALSE((view1 | view2 | view3).contains(other));
ASSERT_FALSE((view1 | view4 | view2).contains(entity));
ASSERT_TRUE((view1 | view4 | view2).contains(other));
}