group: optimized ranges

This commit is contained in:
Michele Caini
2020-07-09 18:18:07 +02:00
parent 1b93a449b2
commit dd28882b27

View File

@@ -17,325 +17,6 @@
namespace entt {
/**
* @brief Group range.
*
* Primary template isn't defined on purpose. All the specializations give a
* compile-time error, but for a few reasonable cases.
*/
template<typename...>
class group_range;
/**
* @brief Non-owning group range.
*
* Iterable object to use to _visit_ a group.
*
* @sa group
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Exclude Types of components used to filter the group.
* @tparam Get Type of components observed by the group.
*/
template<typename Entity, typename... Exclude, typename... Get>
class group_range<Entity, exclude_t<Exclude...>, get_t<Get...>> {
/*! @brief A group is allowed to create ranges. */
friend class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>>;
// I could have used std::conditional_t ...
template<typename Comp>
struct pool { using type = storage<Entity, Comp>; };
// ... if only MSVC didn't have a bug ...
template<typename Comp>
struct pool<const Comp> { using type = const storage<Entity, std::remove_const_t<Comp>>; };
// ... that forces me to do the same in a worse way! :(
template<typename Comp>
using pool_type = typename pool<Comp>::type;
class range_iterator {
friend class group_range<Entity, exclude_t<Exclude...>, entt::get_t<Get...>>;
using it_type = typename sparse_set<Entity>::iterator;
using pool_type = decltype(std::tuple_cat(std::declval<std::conditional_t<ENTT_IS_EMPTY(Get), std::tuple<>, std::tuple<pool_type<Get> *>>>()...));
range_iterator(it_type from, pool_type ref) ENTT_NOEXCEPT
: it{from},
pools{ref}
{}
public:
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(
std::declval<std::tuple<Entity>>(),
std::declval<std::conditional_t<ENTT_IS_EMPTY(Get), std::tuple<>, std::tuple<Get &>>>()...
));
using pointer = void;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
range_iterator() ENTT_NOEXCEPT = default;
range_iterator & operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
range_iterator operator++(int) ENTT_NOEXCEPT {
range_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return std::apply([entt = *it](auto *... cpool) { return reference{entt, cpool->get(entt)...}; }, pools);
}
[[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
it_type it;
pool_type pools;
};
group_range(const sparse_set<Entity> &ref, std::tuple<pool_type<Get> *...> gpools)
: handler{&ref},
pools{gpools}
{}
public:
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Input iterator type. */
using iterator = range_iterator;
/**
* @brief Returns an iterator to the first element.
*
* The returned iterator points to the first element. If the range is empty,
* the returned iterator will be equal to `end()`.
*
* @note
* Iterators stay true to the order imposed to the underlying data
* structures.
*
* @return An iterator to the first element.
*/
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return iterator{handler->begin(), std::tuple_cat([](auto *cpool) {
if constexpr(ENTT_IS_EMPTY(typename std::decay_t<decltype(*cpool)>::object_type)) {
return std::make_tuple();
} else {
return std::make_tuple(cpool);
}
}(std::get<pool_type<Get> *>(pools))...)};
}
/**
* @brief Returns an iterator that is past the last element.
*
* The returned iterator points to the element following the last element.
* Attempting to dereference the returned iterator results in undefined
* behavior.
*
* @note
* Iterators stay true to the order imposed to the underlying data
* structures.
*
* @return An iterator to the element following the last element that has
* the given components.
*/
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return iterator{handler->end(), std::tuple_cat([](auto *cpool) {
if constexpr(ENTT_IS_EMPTY(typename std::decay_t<decltype(*cpool)>::object_type)) {
return std::make_tuple();
} else {
return std::make_tuple(cpool);
}
}(std::get<pool_type<Get> *>(pools))...)};
}
private:
const sparse_set<Entity> *handler;
std::tuple<pool_type<Get> *...> pools;
};
/**
* @brief Owning group range specialization.
*
* Iterable object to use to _visit_ a group.
*
* @sa group
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Exclude Types of components used to filter the group.
* @tparam Get Types of components observed by the group.
* @tparam Owned Types of components owned by the group.
*/
template<typename Entity, typename... Exclude, typename... Get, typename... Owned>
class group_range<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...> {
/*! @brief A group is allowed to create ranges. */
friend class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...>;
// I could have used std::conditional_t ...
template<typename Comp>
struct pool { using type = storage<Entity, Comp>; };
// ... if only MSVC didn't have a bug ...
template<typename Comp>
struct pool<const Comp> { using type = const storage<Entity, std::remove_const_t<Comp>>; };
// ... that forces me to do the same in a worse way! :(
template<typename Comp>
using pool_type = typename pool<Comp>::type;
class range_iterator {
friend class group_range<Entity, exclude_t<Exclude...>, entt::get_t<Get...>, Owned...>;
using it_type = typename sparse_set<Entity>::iterator;
using owned_type = decltype(std::tuple_cat(std::declval<std::conditional_t<ENTT_IS_EMPTY(Owned), std::tuple<>, std::tuple<decltype(std::declval<pool_type<Owned>>().begin())>>>()...));
using get_type = decltype(std::tuple_cat(std::declval<std::conditional_t<ENTT_IS_EMPTY(Get), std::tuple<>, std::tuple<pool_type<Get> *>>>()...));
range_iterator(it_type from, owned_type oref, get_type gref) ENTT_NOEXCEPT
: it{from},
owned{oref},
get{gref}
{}
public:
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(
std::declval<std::tuple<Entity>>(),
std::declval<std::conditional_t<ENTT_IS_EMPTY(Owned), std::tuple<>, std::tuple<Owned &>>>()...,
std::declval<std::conditional_t<ENTT_IS_EMPTY(Get), std::tuple<>, std::tuple<Get &>>>()...
));
using pointer = void;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
range_iterator() ENTT_NOEXCEPT = default;
range_iterator & operator++() ENTT_NOEXCEPT {
return ++it, std::apply([](auto &&... curr) { (++curr, ...); }, owned), *this;
}
range_iterator operator++(int) ENTT_NOEXCEPT {
range_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return std::tuple_cat(
std::make_tuple(*it),
std::apply([](auto &&... curr) { return std::forward_as_tuple(*curr...); }, owned),
std::apply([entt = *it](auto &&... curr) { return std::forward_as_tuple(curr->get(entt)...); }, get)
);
}
[[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
it_type it;
owned_type owned;
get_type get;
};
group_range(std::tuple<pool_type<Owned> *..., pool_type<Get> *...> cpools, const std::size_t &extent)
: pools{cpools},
length{&extent}
{}
public:
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Input iterator type. */
using iterator = range_iterator;
/**
* @brief Returns an iterator to the first element.
*
* The returned iterator points to the first element. If the range is empty,
* the returned iterator will be equal to `end()`.
*
* @note
* Iterators stay true to the order imposed to the underlying data
* structures.
*
* @return An iterator to the first element.
*/
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return iterator{
std::get<0>(pools)->sparse_set<Entity>::end() - *length,
std::tuple_cat([this](auto *cpool) {
if constexpr(ENTT_IS_EMPTY(typename std::decay_t<decltype(*cpool)>::object_type)) {
return std::make_tuple();
} else {
return std::make_tuple(cpool->end() - *length);
}
}(std::get<pool_type<Owned> *>(pools))...),
std::tuple_cat([](auto *cpool) {
if constexpr(ENTT_IS_EMPTY(typename std::decay_t<decltype(*cpool)>::object_type)) {
return std::make_tuple();
} else {
return std::make_tuple(cpool);
}
}(std::get<pool_type<Get> *>(pools))...)
};
}
/**
* @brief Returns an iterator that is past the last element.
*
* The returned iterator points to the element following the last element.
* Attempting to dereference the returned iterator results in undefined
* behavior.
*
* @note
* Iterators stay true to the order imposed to the underlying data
* structures.
*
* @return An iterator to the element following the last element that has
* the given components.
*/
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return iterator{
std::get<0>(pools)->sparse_set<Entity>::end(),
std::tuple_cat([](auto *cpool) {
if constexpr(ENTT_IS_EMPTY(typename std::decay_t<decltype(*cpool)>::object_type)) {
return std::make_tuple();
} else {
return std::make_tuple(cpool->end());
}
}(std::get<pool_type<Owned> *>(pools))...),
std::tuple_cat([](auto *cpool) {
if constexpr(ENTT_IS_EMPTY(typename std::decay_t<decltype(*cpool)>::object_type)) {
return std::make_tuple();
} else {
return std::make_tuple(cpool);
}
}(std::get<pool_type<Get> *>(pools))...)
};
}
private:
const std::tuple<pool_type<Owned> *..., pool_type<Get> *...> pools;
const std::size_t *length;
};
/**
* @brief Group.
*
@@ -399,6 +80,89 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>> {
template<typename Component>
using pool_type = typename pool<Component>::type;
class group_range {
friend class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>>;
class range_iterator {
friend class group_range;
using it_type = typename sparse_set<Entity>::iterator;
using pool_type = decltype(std::tuple_cat(std::declval<std::conditional_t<ENTT_IS_EMPTY(Get), std::tuple<>, std::tuple<pool_type<Get> *>>>()...));
range_iterator(it_type from, pool_type ref) ENTT_NOEXCEPT
: it{from},
pools{ref}
{}
public:
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(
std::declval<std::tuple<Entity>>(),
std::declval<std::conditional_t<ENTT_IS_EMPTY(Get), std::tuple<>, std::tuple<Get &>>>()...
));
using pointer = void;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
range_iterator() ENTT_NOEXCEPT = default;
range_iterator & operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
range_iterator operator++(int) ENTT_NOEXCEPT {
range_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return std::apply([entt = *it](auto *... cpool) { return reference{entt, cpool->get(entt)...}; }, pools);
}
[[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
it_type it;
pool_type pools;
};
group_range(const sparse_set<Entity> &ref, std::tuple<pool_type<Get> *...> gpools)
: handler{&ref},
pools{gpools}
{}
public:
[[nodiscard]] auto begin() const ENTT_NOEXCEPT {
return range_iterator{handler->begin(), std::tuple_cat([](auto *cpool) {
if constexpr(ENTT_IS_EMPTY(typename std::decay_t<decltype(*cpool)>::object_type)) {
return std::make_tuple();
} else {
return std::make_tuple(cpool);
}
}(std::get<pool_type<Get> *>(pools))...)};
}
[[nodiscard]] auto end() const ENTT_NOEXCEPT {
return range_iterator{handler->end(), std::tuple_cat([](auto *cpool) {
if constexpr(ENTT_IS_EMPTY(typename std::decay_t<decltype(*cpool)>::object_type)) {
return std::make_tuple();
} else {
return std::make_tuple(cpool);
}
}(std::get<pool_type<Get> *>(pools))...)};
}
private:
const sparse_set<Entity> *handler;
std::tuple<pool_type<Get> *...> pools;
};
basic_group(sparse_set<Entity> &ref, pool_type<Get> &... gpool) ENTT_NOEXCEPT
: handler{&ref},
pools{&gpool...}
@@ -678,8 +442,8 @@ public:
*
* @return An iterable object to use to _visit_ the group.
*/
group_range<entity_type, exclude_t<Exclude...>, get_t<Get...>> each() const {
return { *handler, pools };
[[nodiscard]] auto each() const ENTT_NOEXCEPT {
return group_range{*handler, pools};
}
/**
@@ -827,6 +591,117 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...> {
template<typename Component>
using component_iterator = decltype(std::declval<pool_type<Component>>().begin());
class group_range {
friend class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...>;
class range_iterator {
friend class group_range;
using it_type = typename sparse_set<Entity>::iterator;
using owned_type = decltype(std::tuple_cat(std::declval<std::conditional_t<ENTT_IS_EMPTY(Owned), std::tuple<>, std::tuple<component_iterator<Owned>>>>()...));
using get_type = decltype(std::tuple_cat(std::declval<std::conditional_t<ENTT_IS_EMPTY(Get), std::tuple<>, std::tuple<pool_type<Get> *>>>()...));
range_iterator(it_type from, owned_type oref, get_type gref) ENTT_NOEXCEPT
: it{from},
owned{oref},
get{gref}
{}
public:
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(
std::declval<std::tuple<Entity>>(),
std::declval<std::conditional_t<ENTT_IS_EMPTY(Owned), std::tuple<>, std::tuple<Owned &>>>()...,
std::declval<std::conditional_t<ENTT_IS_EMPTY(Get), std::tuple<>, std::tuple<Get &>>>()...
));
using pointer = void;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
range_iterator() ENTT_NOEXCEPT = default;
range_iterator & operator++() ENTT_NOEXCEPT {
return ++it, std::apply([](auto &&... curr) { (++curr, ...); }, owned), *this;
}
range_iterator operator++(int) ENTT_NOEXCEPT {
range_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return std::tuple_cat(
std::make_tuple(*it),
std::apply([](auto &&... curr) { return std::forward_as_tuple(*curr...); }, owned),
std::apply([entt = *it](auto &&... curr) { return std::forward_as_tuple(curr->get(entt)...); }, get)
);
}
[[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
it_type it;
owned_type owned;
get_type get;
};
group_range(std::tuple<pool_type<Owned> *..., pool_type<Get> *...> cpools, const std::size_t &extent)
: pools{cpools},
length{&extent}
{}
public:
[[nodiscard]] auto begin() const ENTT_NOEXCEPT {
return range_iterator{
std::get<0>(pools)->sparse_set<Entity>::end() - *length,
std::tuple_cat([this](auto *cpool) {
if constexpr(ENTT_IS_EMPTY(typename std::decay_t<decltype(*cpool)>::object_type)) {
return std::make_tuple();
} else {
return std::make_tuple(cpool->end() - *length);
}
}(std::get<pool_type<Owned> *>(pools))...),
std::tuple_cat([](auto *cpool) {
if constexpr(ENTT_IS_EMPTY(typename std::decay_t<decltype(*cpool)>::object_type)) {
return std::make_tuple();
} else {
return std::make_tuple(cpool);
}
}(std::get<pool_type<Get> *>(pools))...)
};
}
[[nodiscard]] auto end() const ENTT_NOEXCEPT {
return range_iterator{
std::get<0>(pools)->sparse_set<Entity>::end(),
std::tuple_cat([](auto *cpool) {
if constexpr(ENTT_IS_EMPTY(typename std::decay_t<decltype(*cpool)>::object_type)) {
return std::make_tuple();
} else {
return std::make_tuple(cpool->end());
}
}(std::get<pool_type<Owned> *>(pools))...),
std::tuple_cat([](auto *cpool) {
if constexpr(ENTT_IS_EMPTY(typename std::decay_t<decltype(*cpool)>::object_type)) {
return std::make_tuple();
} else {
return std::make_tuple(cpool);
}
}(std::get<pool_type<Get> *>(pools))...)
};
}
private:
const std::tuple<pool_type<Owned> *..., pool_type<Get> *...> pools;
const std::size_t *length;
};
basic_group(const std::size_t &extent, pool_type<Owned> &... opool, pool_type<Get> &... gpool) ENTT_NOEXCEPT
: pools{&opool..., &gpool...},
length{&extent}
@@ -1108,8 +983,8 @@ public:
*
* @return An iterable object to use to _visit_ the group.
*/
group_range<entity_type, exclude_t<Exclude...>, get_t<Get...>, Owned...> each() const {
return { pools, *length };
[[nodiscard]] auto each() const ENTT_NOEXCEPT {
return group_range{pools, *length};
}
/**