review: multi component standard view
This commit is contained in:
@@ -1276,9 +1276,9 @@ entities available for each component and pick up a reference to the smallest
|
||||
set of candidates in order to speed up iterations.<br/>
|
||||
They offer fewer functionalities than their companion views for single
|
||||
component. In particular, a multi component standard view exposes utility
|
||||
functions to reset its internal state (optimization purposes) and to get the
|
||||
estimated number of entities it is going to return. It's also possible to ask a
|
||||
view if it contains a given entity.<br/>
|
||||
functions to get the estimated number of entities it is going to return and to
|
||||
know whether it's empty or not. It's also possible to ask a view if it contains
|
||||
a given entity.<br/>
|
||||
Refer to the [official documentation](https://skypjack.github.io/entt/) for all
|
||||
the details.
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/ident.hpp"
|
||||
#include "entt_traits.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
|
||||
@@ -432,16 +431,13 @@ class View final {
|
||||
class Iterator {
|
||||
using size_type = typename view_type::size_type;
|
||||
|
||||
inline bool valid() const ENTT_NOEXCEPT {
|
||||
bool valid() const ENTT_NOEXCEPT {
|
||||
const auto entity = *begin;
|
||||
const auto sz = size_type(entity & traits_type::entity_mask);
|
||||
auto pos = unchecked.size();
|
||||
|
||||
if(sz < extent) {
|
||||
for(; pos && unchecked[pos-1]->fast(entity); --pos);
|
||||
}
|
||||
|
||||
return !pos;
|
||||
return sz < extent && std::all_of(unchecked.cbegin(), unchecked.cend(), [entity](const view_type *view) {
|
||||
return view->fast(entity);
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -499,42 +495,46 @@ class View final {
|
||||
};
|
||||
|
||||
View(pool_type<Component> &... pools) ENTT_NOEXCEPT
|
||||
: pools{pools...}, view{nullptr}, unchecked{}, idx{}
|
||||
{
|
||||
reset();
|
||||
: pools{pools...}
|
||||
{}
|
||||
|
||||
template<typename Comp>
|
||||
const pool_type<Comp> & pool() const ENTT_NOEXCEPT {
|
||||
return std::get<pool_type<Comp> &>(pools);
|
||||
}
|
||||
|
||||
template<typename Comp, typename Other, typename It>
|
||||
inline std::enable_if_t<std::is_same<Comp, Other>::value, const Other &>
|
||||
get(const It &it, Entity) const { return *it; }
|
||||
template<typename Comp>
|
||||
inline pool_type<Comp> & pool() ENTT_NOEXCEPT {
|
||||
return const_cast<pool_type<Comp> &>(const_cast<const View *>(this)->pool<Comp>());
|
||||
}
|
||||
|
||||
template<typename Comp, typename Other, typename It>
|
||||
inline std::enable_if_t<!std::is_same<Comp, Other>::value, const Other &>
|
||||
get(const It &, Entity entity) const { return std::get<pool_type<Other> &>(pools).get(entity); }
|
||||
|
||||
template<typename Comp, typename Func>
|
||||
void each(Func func) const {
|
||||
const auto extent = std::min({ std::get<pool_type<Component> &>(pools).extent()... });
|
||||
auto &pool = std::get<pool_type<Comp> &>(pools);
|
||||
|
||||
std::for_each(pool.view_type::cbegin(), pool.view_type::cend(), [func = std::move(func), raw = pool.cbegin(), extent, this](const auto entity) mutable {
|
||||
const auto sz = size_type(entity & traits_type::entity_mask);
|
||||
|
||||
if(sz < extent) {
|
||||
auto pos = unchecked.size();
|
||||
|
||||
for(; pos && unchecked[pos-1]->fast(entity); --pos);
|
||||
|
||||
if(!pos) {
|
||||
// avoided indirections due to the sparse set for the pivot (this-> required because of GCC 6)
|
||||
func(entity, this->get<Comp, Component>(raw, entity)...);
|
||||
}
|
||||
}
|
||||
|
||||
++raw;
|
||||
const view_type * candidate() const ENTT_NOEXCEPT {
|
||||
return std::min({ static_cast<const view_type *>(&pool<Component>())... }, [](const auto *lhs, const auto *rhs) {
|
||||
return lhs->size() < rhs->size();
|
||||
});
|
||||
}
|
||||
|
||||
unchecked_type unchecked(const view_type *view) const ENTT_NOEXCEPT {
|
||||
unchecked_type other{};
|
||||
std::size_t pos{};
|
||||
using accumulator_type = const view_type *[];
|
||||
accumulator_type accumulator = { (&pool<Component>() == view ? view : other[pos++] = &pool<Component>())... };
|
||||
(void)accumulator;
|
||||
return other;
|
||||
}
|
||||
|
||||
typename view_type::size_type extent() const ENTT_NOEXCEPT {
|
||||
return std::min({ pool<Component>().extent()... });
|
||||
}
|
||||
|
||||
template<typename Comp, typename Other>
|
||||
inline std::enable_if_t<std::is_same<Comp, Other>::value, const Other &>
|
||||
get(const typename pool_type<Comp>::const_iterator_type &it, Entity) const ENTT_NOEXCEPT { return *it; }
|
||||
|
||||
template<typename Comp, typename Other>
|
||||
inline std::enable_if_t<!std::is_same<Comp, Other>::value, const Other &>
|
||||
get(const typename pool_type<Comp>::const_iterator_type &, Entity entity) const ENTT_NOEXCEPT { return pool<Other>().get(entity); }
|
||||
|
||||
public:
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = Iterator;
|
||||
@@ -550,7 +550,7 @@ public:
|
||||
* @return Estimated number of entities that have the given components.
|
||||
*/
|
||||
size_type size() const ENTT_NOEXCEPT {
|
||||
return view->size();
|
||||
return std::min({ pool<Component>().size()... });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -558,7 +558,7 @@ public:
|
||||
* @return True if the view is definitely empty, false otherwise.
|
||||
*/
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
return view->empty();
|
||||
return std::max({ pool<Component>().empty()... });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -576,8 +576,8 @@ public:
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
const_iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
const auto extent = std::min({ std::get<pool_type<Component> &>(pools).extent()... });
|
||||
return iterator_type{ unchecked, extent, view->cbegin(), view->cend() };
|
||||
const auto *view = candidate();
|
||||
return iterator_type{ unchecked(view), extent(), view->cbegin(), view->cend() };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -614,8 +614,8 @@ public:
|
||||
* given components.
|
||||
*/
|
||||
const_iterator_type cend() const ENTT_NOEXCEPT {
|
||||
const auto extent = std::min({ std::get<pool_type<Component> &>(pools).extent()... });
|
||||
return iterator_type{ unchecked, extent, view->cend(), view->cend() };
|
||||
const auto *view = candidate();
|
||||
return iterator_type{ unchecked(view), extent(), view->cend(), view->cend() };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -643,15 +643,8 @@ public:
|
||||
* @return True if the view contains the given entity, false otherwise.
|
||||
*/
|
||||
bool contains(entity_type entity) const ENTT_NOEXCEPT {
|
||||
const auto extent = std::min({ std::get<pool_type<Component> &>(pools).extent()... });
|
||||
const auto sz = size_type(entity & traits_type::entity_mask);
|
||||
auto pos = unchecked.size();
|
||||
|
||||
if(sz < extent && view->has(entity) && (view->data()[view->get(entity)] == entity)) {
|
||||
for(; pos && unchecked[pos-1]->fast(entity); --pos);
|
||||
}
|
||||
|
||||
return !pos;
|
||||
return sz < extent() && std::min({ (pool<Component>().has(entity) && (pool<Component>().data()[pool<Component>().view_type::get(entity)] == entity))... });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -674,7 +667,7 @@ public:
|
||||
template<typename Comp>
|
||||
const Comp & get(entity_type entity) const ENTT_NOEXCEPT {
|
||||
assert(contains(entity));
|
||||
return std::get<pool_type<Comp> &>(pools).get(entity);
|
||||
return pool<Comp>().get(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -764,10 +757,23 @@ public:
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
inline void each(Func func) const {
|
||||
constexpr auto indexes = ident<Component...>;
|
||||
void each(Func func) const {
|
||||
auto iterate = [&func, this](const auto &cpool) {
|
||||
std::for_each(cpool.view_type::cbegin(), cpool.view_type::cend(), [&func, raw = cpool.cbegin(), unchecked = this->unchecked(&cpool), extent = this->extent(), this](const auto entity) mutable {
|
||||
const auto sz = size_type(entity & traits_type::entity_mask);
|
||||
|
||||
if(sz < extent && std::all_of(unchecked.cbegin(), unchecked.cend(), [entity](const view_type *view) { return view->fast(entity); })) {
|
||||
// avoided indirections due to the sparse set for the pivot type
|
||||
func(entity, this->get<typename std::decay_t<decltype(cpool)>::object_type, Component>(raw, entity)...);
|
||||
}
|
||||
|
||||
++raw;
|
||||
});
|
||||
};
|
||||
|
||||
const auto *view = candidate();
|
||||
using accumulator_type = int[];
|
||||
accumulator_type accumulator = { (indexes.template get<Component>() == idx ? (each<Component>(std::move(func)), 0) : 0)... };
|
||||
accumulator_type accumulator = { (&pool<Component>() == view ? (iterate(pool<Component>()), 0) : 0)... };
|
||||
(void)accumulator;
|
||||
}
|
||||
|
||||
@@ -794,41 +800,8 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets the view and reinitializes it.
|
||||
*
|
||||
* A multi component view keeps a reference to the smallest set of candidate
|
||||
* entities to iterate. Resetting a view means querying the underlying data
|
||||
* structures and reinitializing the view.<br/>
|
||||
* Use it only if copies of views are stored around and there is a
|
||||
* possibility that a component has become the best candidate in the
|
||||
* meantime.
|
||||
*/
|
||||
void reset() {
|
||||
using accumulator_type = size_type[];
|
||||
size_type sz = std::max({ std::get<pool_type<Component> &>(pools).size()... }) + std::size_t{1};
|
||||
size_type next{};
|
||||
|
||||
auto probe = [this](auto sz, const auto &pool) {
|
||||
return pool.size() < sz ? (view = &pool, pool.size()) : sz;
|
||||
};
|
||||
|
||||
auto filter = [this](auto next, const auto &pool) {
|
||||
return (view == &pool) ? (idx = next) : (unchecked[next++] = &pool, next);
|
||||
};
|
||||
|
||||
accumulator_type probing = { (sz = probe(sz, std::get<pool_type<Component> &>(pools)))... };
|
||||
accumulator_type filtering = { (next = filter(next, std::get<pool_type<Component> &>(pools)))... };
|
||||
|
||||
(void)filtering;
|
||||
(void)probing;
|
||||
}
|
||||
|
||||
private:
|
||||
const pattern_type pools;
|
||||
const view_type *view;
|
||||
unchecked_type unchecked;
|
||||
size_type idx;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -175,10 +175,6 @@ TEST(View, MultipleComponent) {
|
||||
|
||||
registry.remove<char>(e0);
|
||||
registry.remove<char>(e1);
|
||||
view.reset();
|
||||
|
||||
ASSERT_EQ(view.begin(), view.end());
|
||||
ASSERT_TRUE(view.empty());
|
||||
}
|
||||
|
||||
TEST(View, MultipleComponentBeginEnd) {
|
||||
|
||||
Reference in New Issue
Block a user