view/group: all iterators are at least bidirectional (close #370)

This commit is contained in:
Michele Caini
2019-12-07 23:41:59 +01:00
parent 46db75308c
commit 5a1af60357
6 changed files with 81 additions and 51 deletions

View File

@@ -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.

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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) {