view/group: all iterators are at least bidirectional (close #370)
This commit is contained in:
@@ -1734,9 +1734,9 @@ expedients.
|
||||
|
||||
## Iterators
|
||||
|
||||
A special mention is needed for the iterators returned by the views and the
|
||||
groups. Most of the time they meet the requirements of random access iterators,
|
||||
in all cases they meet at least the requirements of forward iterators.<br/>
|
||||
A special mention is needed for the iterators returned by views and groups. Most
|
||||
of the times they meet the requirements of random access iterators, in all cases
|
||||
they meet at least the requirements of bidirectional iterators.<br/>
|
||||
In other terms, they are suitable for use with the parallel algorithms of the
|
||||
standard library. If it's not clear, this is a great thing.
|
||||
|
||||
|
||||
@@ -64,20 +64,20 @@ class basic_runtime_view {
|
||||
class iterator {
|
||||
friend class basic_runtime_view<Entity>;
|
||||
|
||||
iterator(underlying_iterator_type first, underlying_iterator_type last, const sparse_set<Entity> * const *others, const sparse_set<Entity> * const *length) ENTT_NOEXCEPT
|
||||
: begin{first},
|
||||
end{last},
|
||||
from{others},
|
||||
to{length}
|
||||
using direct_type = std::vector<const sparse_set<Entity> *>;
|
||||
|
||||
iterator(const direct_type *all, underlying_iterator_type curr) ENTT_NOEXCEPT
|
||||
: pools{all},
|
||||
it{curr}
|
||||
{
|
||||
if(begin != end && !valid()) {
|
||||
if(it != (*pools)[0]->end() && !valid()) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
bool valid() const {
|
||||
return std::all_of(from, to, [entt = *begin](const auto *view) {
|
||||
return view->has(entt);
|
||||
return std::all_of(pools->begin()++, pools->end(), [entt = *it](const auto *curr) {
|
||||
return curr->has(entt);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -86,12 +86,13 @@ class basic_runtime_view {
|
||||
using value_type = typename underlying_iterator_type::value_type;
|
||||
using pointer = typename underlying_iterator_type::pointer;
|
||||
using reference = typename underlying_iterator_type::reference;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
|
||||
iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
iterator & operator++() {
|
||||
return (++begin != end && !valid()) ? ++(*this) : *this;
|
||||
while(++it != (*pools)[0]->end() && !valid());
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) {
|
||||
@@ -99,8 +100,18 @@ class basic_runtime_view {
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
iterator & operator--() ENTT_NOEXCEPT {
|
||||
while(--it != (*pools)[0]->begin() && !valid());
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator--(int) ENTT_NOEXCEPT {
|
||||
iterator orig = *this;
|
||||
return --(*this), orig;
|
||||
}
|
||||
|
||||
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.begin == begin;
|
||||
return other.it == it;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
@@ -108,7 +119,7 @@ class basic_runtime_view {
|
||||
}
|
||||
|
||||
pointer operator->() const {
|
||||
return begin.operator->();
|
||||
return it.operator->();
|
||||
}
|
||||
|
||||
reference operator*() const {
|
||||
@@ -116,10 +127,8 @@ class basic_runtime_view {
|
||||
}
|
||||
|
||||
private:
|
||||
underlying_iterator_type begin;
|
||||
underlying_iterator_type end;
|
||||
const sparse_set<Entity> * const *from;
|
||||
const sparse_set<Entity> * const *to;
|
||||
const direct_type *pools;
|
||||
underlying_iterator_type it;
|
||||
};
|
||||
|
||||
basic_runtime_view(std::vector<const sparse_set<Entity> *> others) ENTT_NOEXCEPT
|
||||
@@ -179,9 +188,7 @@ public:
|
||||
iterator_type it{};
|
||||
|
||||
if(valid()) {
|
||||
const auto &pool = *pools.front();
|
||||
const auto * const *data = pools.data();
|
||||
it = { pool.begin(), pool.end(), data + 1, data + pools.size() };
|
||||
it = { &pools, pools[0]->begin() };
|
||||
}
|
||||
|
||||
return it;
|
||||
@@ -206,8 +213,7 @@ public:
|
||||
iterator_type it{};
|
||||
|
||||
if(valid()) {
|
||||
const auto &pool = *pools.front();
|
||||
it = { pool.end(), pool.end(), nullptr, nullptr };
|
||||
it = { &pools, pools[0]->end() };
|
||||
}
|
||||
|
||||
return it;
|
||||
|
||||
@@ -55,10 +55,10 @@ class sparse_set {
|
||||
class iterator {
|
||||
friend class sparse_set<Entity>;
|
||||
|
||||
using direct_type = const std::vector<Entity>;
|
||||
using direct_type = std::vector<Entity>;
|
||||
using index_type = typename traits_type::difference_type;
|
||||
|
||||
iterator(direct_type *ref, const index_type idx) ENTT_NOEXCEPT
|
||||
iterator(const direct_type *ref, const index_type idx) ENTT_NOEXCEPT
|
||||
: direct{ref}, index{idx}
|
||||
{}
|
||||
|
||||
@@ -149,7 +149,7 @@ class sparse_set {
|
||||
}
|
||||
|
||||
private:
|
||||
direct_type *direct;
|
||||
const direct_type *direct;
|
||||
index_type index;
|
||||
};
|
||||
|
||||
|
||||
@@ -81,24 +81,25 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
|
||||
using underlying_iterator_type = typename sparse_set<Entity>::iterator_type;
|
||||
using unchecked_type = std::array<const sparse_set<Entity> *, (sizeof...(Component) - 1)>;
|
||||
using filter_type = std::array<const sparse_set<Entity> *, sizeof...(Exclude)>;
|
||||
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
|
||||
|
||||
class iterator {
|
||||
friend class basic_view<Entity, exclude_t<Exclude...>, Component...>;
|
||||
|
||||
iterator(underlying_iterator_type first, underlying_iterator_type last, unchecked_type other, filter_type ignore) ENTT_NOEXCEPT
|
||||
: begin{first},
|
||||
end{last},
|
||||
iterator(const sparse_set<Entity> *candidate, unchecked_type other, filter_type ignore, underlying_iterator_type curr) ENTT_NOEXCEPT
|
||||
: view{candidate},
|
||||
unchecked{other},
|
||||
filter{ignore}
|
||||
filter{ignore},
|
||||
it{curr}
|
||||
{
|
||||
if(begin != end && !valid()) {
|
||||
if(it != view->end() && !valid()) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
bool valid() const {
|
||||
return std::all_of(unchecked.cbegin(), unchecked.cend(), [this](const sparse_set<Entity> *view) { return view->has(*begin); })
|
||||
&& std::none_of(filter.cbegin(), filter.cend(), [this](const sparse_set<Entity> *view) { return view->has(*begin); });
|
||||
return std::all_of(unchecked.cbegin(), unchecked.cend(), [entt = *it](const sparse_set<Entity> *curr) { return curr->has(entt); })
|
||||
&& std::none_of(filter.cbegin(), filter.cend(), [entt = *it](const sparse_set<Entity> *curr) { return curr->has(entt); });
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -106,12 +107,13 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
|
||||
using value_type = typename underlying_iterator_type::value_type;
|
||||
using pointer = typename underlying_iterator_type::pointer;
|
||||
using reference = typename underlying_iterator_type::reference;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
|
||||
iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
iterator & operator++() {
|
||||
return (++begin != end && !valid()) ? ++(*this) : *this;
|
||||
while(++it != view->end() && !valid());
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) {
|
||||
@@ -119,8 +121,18 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
iterator & operator--() ENTT_NOEXCEPT {
|
||||
while(--it != view->begin() && !valid());
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator--(int) ENTT_NOEXCEPT {
|
||||
iterator orig = *this;
|
||||
return --(*this), orig;
|
||||
}
|
||||
|
||||
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.begin == begin;
|
||||
return other.it == it;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
@@ -128,7 +140,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
|
||||
}
|
||||
|
||||
pointer operator->() const {
|
||||
return begin.operator->();
|
||||
return it.operator->();
|
||||
}
|
||||
|
||||
reference operator*() const {
|
||||
@@ -136,10 +148,10 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
|
||||
}
|
||||
|
||||
private:
|
||||
underlying_iterator_type begin;
|
||||
underlying_iterator_type end;
|
||||
const sparse_set<Entity> *view;
|
||||
unchecked_type unchecked;
|
||||
filter_type filter;
|
||||
underlying_iterator_type it;
|
||||
};
|
||||
|
||||
// we could use pool_type<Component> *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug)
|
||||
@@ -305,7 +317,7 @@ public:
|
||||
iterator_type begin() const {
|
||||
const auto *view = candidate();
|
||||
const filter_type ignore{std::get<pool_type<Exclude> *>(filter)...};
|
||||
return iterator_type{view->begin(), view->end(), unchecked(view), ignore};
|
||||
return iterator_type{view, unchecked(view), ignore, view->begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,7 +338,7 @@ public:
|
||||
iterator_type end() const {
|
||||
const auto *view = candidate();
|
||||
const filter_type ignore{std::get<pool_type<Exclude> *>(filter)...};
|
||||
return iterator_type{view->end(), view->end(), unchecked(view), ignore};
|
||||
return iterator_type{view, unchecked(view), ignore, view->end()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -338,7 +350,7 @@ public:
|
||||
iterator_type find(const entity_type entt) const {
|
||||
const auto *view = candidate();
|
||||
const filter_type ignore{std::get<pool_type<Exclude> *>(filter)...};
|
||||
iterator_type it{view->find(entt), view->end(), unchecked(view), ignore};
|
||||
iterator_type it{view, unchecked(view), ignore, view->find(entt)};
|
||||
return (it != end() && *it == entt) ? it : end();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,13 +26,13 @@ TEST(RuntimeView, Functionalities) {
|
||||
|
||||
registry.assign<char>(e1);
|
||||
|
||||
auto it = registry.runtime_view(std::begin(types), std::end(types)).begin();
|
||||
auto it = view.begin();
|
||||
|
||||
ASSERT_EQ(*it, e1);
|
||||
ASSERT_EQ(++it, (registry.runtime_view(std::begin(types), std::end(types)).end()));
|
||||
ASSERT_EQ(++it, (view.end()));
|
||||
|
||||
ASSERT_NO_THROW((registry.runtime_view(std::begin(types), std::end(types)).begin()++));
|
||||
ASSERT_NO_THROW((++registry.runtime_view(std::begin(types), std::end(types)).begin()));
|
||||
ASSERT_NO_THROW((view.begin()++));
|
||||
ASSERT_NO_THROW((++view.begin()));
|
||||
|
||||
ASSERT_NE(view.begin(), view.end());
|
||||
ASSERT_EQ(view.size(), decltype(view.size()){1});
|
||||
@@ -67,8 +67,14 @@ TEST(RuntimeView, Iterator) {
|
||||
ASSERT_EQ(end, view.end());
|
||||
ASSERT_NE(begin, end);
|
||||
|
||||
ASSERT_EQ(view.begin()++, view.begin());
|
||||
ASSERT_EQ(++view.begin(), view.end());
|
||||
ASSERT_EQ(begin++, view.begin());
|
||||
ASSERT_EQ(begin--, view.end());
|
||||
|
||||
ASSERT_EQ(++begin, view.end());
|
||||
ASSERT_EQ(--begin, view.begin());
|
||||
|
||||
ASSERT_EQ(*begin, entity);
|
||||
ASSERT_EQ(*begin.operator->(), entity);
|
||||
}
|
||||
|
||||
TEST(RuntimeView, Contains) {
|
||||
|
||||
@@ -286,8 +286,14 @@ TEST(MultiComponentView, Iterator) {
|
||||
ASSERT_EQ(end, view.end());
|
||||
ASSERT_NE(begin, end);
|
||||
|
||||
ASSERT_EQ(view.begin()++, view.begin());
|
||||
ASSERT_EQ(++view.begin(), view.end());
|
||||
ASSERT_EQ(begin++, view.begin());
|
||||
ASSERT_EQ(begin--, view.end());
|
||||
|
||||
ASSERT_EQ(++begin, view.end());
|
||||
ASSERT_EQ(--begin, view.begin());
|
||||
|
||||
ASSERT_EQ(*begin, entity);
|
||||
ASSERT_EQ(*begin.operator->(), entity);
|
||||
}
|
||||
|
||||
TEST(MultiComponentView, Contains) {
|
||||
|
||||
Reference in New Issue
Block a user