views: const, non-const and all in between (fix #152)
This commit is contained in:
@@ -25,13 +25,14 @@
|
||||
* [Dependency function](#dependency-function)
|
||||
* [Labels](#labels)
|
||||
* [Null entity](#null-entity)
|
||||
* [View: to persist or not to persist?](#view-to-persist-or-not-to-persist)
|
||||
* [Views: pay for what you use](#views-pay-for-what-you-use)
|
||||
* [Standard View](#standard-view)
|
||||
* [Single component standard view](#single-component-standard-view)
|
||||
* [Multi component standard view](#multi-component-standard-view)
|
||||
* [Persistent View](#persistent-view)
|
||||
* [Raw View](#raw-view)
|
||||
* [Runtime View](#runtime-view)
|
||||
* [Types: const, non-const and all in between](#types-const-non-const-and-all-in-between)
|
||||
* [Give me everything](#give-me-everything)
|
||||
* [Iterations: what is allowed and what is not](#iterations-what-is-allowed-and-what-is-not)
|
||||
* [Multithreading](#multithreading)
|
||||
@@ -798,7 +799,7 @@ const auto entity = registry.create();
|
||||
const bool null = (entity == entt::null);
|
||||
```
|
||||
|
||||
# View: to persist or not to persist?
|
||||
# Views: pay for what you use
|
||||
|
||||
First of all, it is worth answering an obvious question: why views?<br/>
|
||||
Roughly speaking, they are a good tool to enforce single responsibility. A
|
||||
@@ -1185,6 +1186,60 @@ well suited to plugin systems and mods in general. Where possible, don't use
|
||||
runtime views, as their performance are slightly inferior to those of the other
|
||||
views.
|
||||
|
||||
# Types: const, non-const and all in between
|
||||
|
||||
The `registry` class offers two overloads for most of the member functions used
|
||||
to construct views: a const one and a non-const one. The former accepts both
|
||||
const and non-const types as template parameters, the latter accepts only const
|
||||
types instead.<br/>
|
||||
It means that views can be constructed also from a const registry and they
|
||||
require to propagate the constness of the registry to the types used to
|
||||
construct the views themselves:
|
||||
|
||||
```cpp
|
||||
entt::view<const position, const velocity> view = std::as_const(registry).view<const position, const velocity>();
|
||||
```
|
||||
|
||||
Consider the following definition for a non-const view instead:
|
||||
|
||||
```cpp
|
||||
entt::view<position, const velocity> view = registry.view<position, const velocity>();
|
||||
```
|
||||
|
||||
In the example above, `view` can be used to access either read-only or writable
|
||||
`position` components while `velocity` components are read-only in all
|
||||
cases.<br/>
|
||||
In other terms, these statements are all valid:
|
||||
|
||||
```cpp
|
||||
position &pos = view.get<position>(entity);
|
||||
const position &cpos = view.get<const position>(entity);
|
||||
const velocity &cpos = view.get<const velocity>(entity);
|
||||
std::tuple<position &, const velocity &> tup = view.get<position, const velocity>(entity);
|
||||
std::tuple<const position &, const velocity &> ctup = view.get<const position, const velocity>(entity);
|
||||
```
|
||||
|
||||
It's not possible to get non-const references to `velocity` components from the
|
||||
same view instead and these will result in compilation errors:
|
||||
|
||||
```cpp
|
||||
velocity &cpos = view.get<velocity>(entity);
|
||||
std::tuple<position &, velocity &> tup = view.get<position, velocity>(entity);
|
||||
std::tuple<const position &, velocity &> ctup = view.get<const position, velocity>(entity);
|
||||
```
|
||||
|
||||
Similarly, the `each` member functions will propagate constness to the type of
|
||||
the components returned during iterations:
|
||||
|
||||
```cpp
|
||||
view.each([](const auto entity, position &pos, const velocity &vel) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
Obviously, a caller can still refer to the `position` components through a const
|
||||
reference because of the rules of the language that fortunately already allow
|
||||
it.
|
||||
|
||||
## Give me everything
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ public:
|
||||
if constexpr(sizeof...(Component) == 1) {
|
||||
return (std::as_const(*reg).template get<component_wrapper<Component...>>(entity).component);
|
||||
} else {
|
||||
return std::tuple<const Component &...>{get<Component>()...};
|
||||
return std::tuple<std::add_const_t<Component> &...>{get<Component>()...};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ public:
|
||||
const auto *wrapper = reg->template try_get<component_wrapper<Component...>>(entity);
|
||||
return wrapper ? &wrapper->component : nullptr;
|
||||
} else {
|
||||
return std::tuple<const Component *...>{try_get<Component>()...};
|
||||
return std::tuple<std::add_const_t<Component> *...>{try_get<Component>()...};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,42 +110,37 @@ class registry {
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
inline const component_pool<Component> & pool() const ENTT_NOEXCEPT {
|
||||
inline auto & pool() const ENTT_NOEXCEPT {
|
||||
assert(managed<Component>());
|
||||
return static_cast<const component_pool<Component> &>(*pools[component_family::type<Component>]);
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
inline component_pool<Component> & pool() ENTT_NOEXCEPT {
|
||||
return const_cast<component_pool<Component> &>(std::as_const(*this).template pool<Component>());
|
||||
return static_cast<component_pool<std::decay_t<Component>> &>(*pools[component_family::type<Component>]);
|
||||
}
|
||||
|
||||
template<typename Comp, std::size_t Index, typename... Component, std::size_t... Indexes>
|
||||
void connect(std::index_sequence<Indexes...>) {
|
||||
void connect(std::index_sequence<Indexes...>) const {
|
||||
pool<Comp>().construction().template connect<®istry::creating<®istry::has<std::tuple_element_t<(Indexes < Index ? Indexes : (Indexes+1)), std::tuple<Component...>>...>, Component...>>();
|
||||
pool<Comp>().destruction().template connect<®istry::destroying<Comp, Index, Component...>>();
|
||||
}
|
||||
|
||||
template<typename... Component, std::size_t... Indexes>
|
||||
void connect(std::index_sequence<Indexes...>) {
|
||||
void connect(std::index_sequence<Indexes...>) const {
|
||||
(assure<Component>(), ...);
|
||||
(connect<Component, Indexes, Component...>(std::make_index_sequence<sizeof...(Component)-1>{}), ...);
|
||||
}
|
||||
|
||||
template<typename Comp, std::size_t Index, typename... Component, std::size_t... Indexes>
|
||||
void disconnect(std::index_sequence<Indexes...>) {
|
||||
void disconnect(std::index_sequence<Indexes...>) const {
|
||||
pool<Comp>().construction().template disconnect<®istry::creating<®istry::has<std::tuple_element_t<(Indexes < Index ? Indexes : (Indexes+1)), std::tuple<Component...>>...>, Component...>>();
|
||||
pool<Comp>().destruction().template disconnect<®istry::destroying<Comp, Index, Component...>>();
|
||||
}
|
||||
|
||||
template<typename... Component, std::size_t... Indexes>
|
||||
void disconnect(std::index_sequence<Indexes...>) {
|
||||
void disconnect(std::index_sequence<Indexes...>) const {
|
||||
// if a set exists, pools have already been created for it
|
||||
(disconnect<Component, Indexes, Component...>(std::make_index_sequence<sizeof...(Component)-1>{}), ...);
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
void assure() {
|
||||
void assure() const {
|
||||
const auto ctype = component_family::type<Component>;
|
||||
|
||||
if(!(ctype < pools.size())) {
|
||||
@@ -153,7 +148,7 @@ class registry {
|
||||
}
|
||||
|
||||
if(!pools[ctype]) {
|
||||
pools[ctype] = std::make_unique<component_pool<Component>>(this);
|
||||
pools[ctype] = std::make_unique<component_pool<std::decay_t<Component>>>(const_cast<registry *>(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +300,7 @@ public:
|
||||
* @return A pointer to the array of components of the given type.
|
||||
*/
|
||||
template<typename Component>
|
||||
const Component * raw() const ENTT_NOEXCEPT {
|
||||
std::add_const_t<Component> * raw() const ENTT_NOEXCEPT {
|
||||
return managed<Component>() ? pool<Component>().raw() : nullptr;
|
||||
}
|
||||
|
||||
@@ -478,6 +473,7 @@ public:
|
||||
*/
|
||||
template<typename It>
|
||||
void create(It first, It last) {
|
||||
static_assert(std::is_convertible_v<entity_type, typename std::iterator_traits<It>::value_type>);
|
||||
const auto length = size_type(last - first);
|
||||
const auto sz = std::min(available, length);
|
||||
|
||||
@@ -646,9 +642,9 @@ public:
|
||||
assert((managed<Component>() && ...));
|
||||
|
||||
if constexpr(sizeof...(Component) == 1) {
|
||||
return pool<Component...>().get(entity);
|
||||
return std::as_const(pool<Component...>()).get(entity);
|
||||
} else {
|
||||
return std::tuple<const Component &...>{get<Component>(entity)...};
|
||||
return std::tuple<std::add_const_t<Component> &...>{get<Component>(entity)...};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,9 +720,9 @@ public:
|
||||
assert(valid(entity));
|
||||
|
||||
if constexpr(sizeof...(Component) == 1) {
|
||||
return managed<Component...>() ? pool<Component...>().try_get(entity) : nullptr;
|
||||
return managed<Component...>() ? std::as_const(pool<Component...>()).try_get(entity) : nullptr;
|
||||
} else {
|
||||
return std::tuple<const Component *...>{try_get<Component>(entity)...};
|
||||
return std::tuple<std::add_const_t<Component> *...>{try_get<Component>(entity)...};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,7 +769,7 @@ public:
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
Component & replace(const entity_type entity, Args &&... args) {
|
||||
return (get<Component>(entity) = Component{std::forward<Args>(args)...});
|
||||
return (pool<Component>().get(entity) = std::decay_t<Component>{std::forward<Args>(args)...});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -808,7 +804,7 @@ public:
|
||||
auto &cpool = pool<Component>();
|
||||
|
||||
return cpool.has(entity)
|
||||
? cpool.get(entity) = Component{std::forward<Args>(args)...}
|
||||
? cpool.get(entity) = std::decay_t<Component>{std::forward<Args>(args)...}
|
||||
: cpool.construct(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -1042,6 +1038,8 @@ public:
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
static_assert(std::is_invocable_v<Func, entity_type>);
|
||||
|
||||
if(available) {
|
||||
for(auto pos = entities.size(); pos; --pos) {
|
||||
const auto curr = entity_type(pos - 1);
|
||||
@@ -1097,6 +1095,8 @@ public:
|
||||
*/
|
||||
template<typename Func>
|
||||
void orphans(Func func) const {
|
||||
static_assert(std::is_invocable_v<Func, entity_type>);
|
||||
|
||||
each([func = std::move(func), this](const auto entity) {
|
||||
if(orphan(entity)) {
|
||||
func(entity);
|
||||
@@ -1144,6 +1144,46 @@ public:
|
||||
return { &pool<Component>()... };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a standard view for the given components.
|
||||
*
|
||||
* This kind of views are created on the fly and share with the registry its
|
||||
* internal data structures.<br/>
|
||||
* Feel free to discard a view after the use. Creating and destroying a view
|
||||
* is an incredibly cheap operation because they do not require any type of
|
||||
* initialization.<br/>
|
||||
* As a rule of thumb, storing a view should never be an option.
|
||||
*
|
||||
* Standard views do their best to iterate the smallest set of candidate
|
||||
* entities. In particular:
|
||||
*
|
||||
* * Single component views are incredibly fast and iterate a packed array
|
||||
* of entities, all of which has the given component.
|
||||
* * Multi component views look at the number of entities available for each
|
||||
* component and pick up a reference to the smallest set of candidates to
|
||||
* test for the given components.
|
||||
*
|
||||
* @note
|
||||
* Multi component views are pretty fast. However their performance tend to
|
||||
* degenerate when the number of components to iterate grows up and the most
|
||||
* of the entities have all the given components.<br/>
|
||||
* To get a performance boost, consider using a persistent_view instead.
|
||||
*
|
||||
* @sa view
|
||||
* @sa view<Entity, Component>
|
||||
* @sa persistent_view
|
||||
* @sa raw_view
|
||||
* @sa runtime_view
|
||||
*
|
||||
* @tparam Component Type of components used to construct the view.
|
||||
* @return A newly created standard view.
|
||||
*/
|
||||
template<typename... Component>
|
||||
inline entt::view<Entity, Component...> view() const {
|
||||
static_assert(std::conjunction_v<std::is_const<Component>...>);
|
||||
return const_cast<registry *>(this)->view<Component...>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a persistent view for the given components.
|
||||
*
|
||||
@@ -1188,24 +1228,67 @@ public:
|
||||
static_assert(sizeof...(Component) > 1);
|
||||
const auto htype = handler_family::type<Component...>;
|
||||
|
||||
if(!(htype < handlers.size() && handlers[htype])) {
|
||||
if(!(htype < handlers.size())) {
|
||||
handlers.resize(htype + 1);
|
||||
}
|
||||
if(!(htype < handlers.size())) {
|
||||
handlers.resize(htype + 1);
|
||||
}
|
||||
|
||||
if(!handlers[htype]) {
|
||||
(assure<Component>(), ...);
|
||||
connect<Component...>(std::make_index_sequence<sizeof...(Component)>{});
|
||||
handlers[htype] = std::make_unique<handler_type<sizeof...(Component)>>();
|
||||
}
|
||||
if(!handlers[htype]) {
|
||||
(assure<Component>(), ...);
|
||||
connect<Component...>(std::make_index_sequence<sizeof...(Component)>{});
|
||||
handlers[htype] = std::make_unique<handler_type<sizeof...(Component)>>();
|
||||
}
|
||||
|
||||
return {
|
||||
static_cast<handler_type<sizeof...(Component)> *>(handlers[htype].get()),
|
||||
static_cast<handler_type<sizeof...(Component)> *>(handlers[handler_family::type<Component...>].get()),
|
||||
&pool<Component>()...
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a persistent view for the given components.
|
||||
*
|
||||
* This kind of views are created on the fly and share with the registry its
|
||||
* internal data structures.<br/>
|
||||
* Feel free to discard a view after the use. Creating and destroying a view
|
||||
* is an incredibly cheap operation because they do not require any type of
|
||||
* initialization.<br/>
|
||||
* As a rule of thumb, storing a view should never be an option.
|
||||
*
|
||||
* Persistent views are the right choice to iterate entities when the number
|
||||
* of components grows up and the most of the entities have all the given
|
||||
* components.<br/>
|
||||
* However they have also drawbacks:
|
||||
*
|
||||
* * Each kind of persistent view requires a dedicated data structure that
|
||||
* is allocated within the registry and it increases memory pressure.
|
||||
* * Internal data structures used to construct persistent views must be
|
||||
* kept updated and it affects slightly construction and destruction of
|
||||
* entities and components.
|
||||
*
|
||||
* That being said, persistent views are an incredibly powerful tool if used
|
||||
* with care and offer a boost of performance undoubtedly.
|
||||
*
|
||||
* @note
|
||||
* Consider to use the `prepare` member function to initialize the internal
|
||||
* data structures used by persistent views when the registry is still
|
||||
* empty. Initialization could be a costly operation otherwise and it will
|
||||
* be performed the very first time each view is created.
|
||||
*
|
||||
* @sa view
|
||||
* @sa view<Entity, Component>
|
||||
* @sa persistent_view
|
||||
* @sa raw_view
|
||||
* @sa runtime_view
|
||||
*
|
||||
* @tparam Component Types of components used to construct the view.
|
||||
* @return A newly created persistent view.
|
||||
*/
|
||||
template<typename... Component>
|
||||
inline entt::persistent_view<Entity, Component...> persistent_view() const {
|
||||
static_assert(std::conjunction_v<std::is_const<Component>...>);
|
||||
return const_cast<registry *>(this)->persistent_view<Component...>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a raw view for the given component.
|
||||
*
|
||||
@@ -1235,6 +1318,35 @@ public:
|
||||
return { &pool<Component>() };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a raw view for the given component.
|
||||
*
|
||||
* This kind of views are created on the fly and share with the registry its
|
||||
* internal data structures.<br/>
|
||||
* Feel free to discard a view after the use. Creating and destroying a view
|
||||
* is an incredibly cheap operation because they do not require any type of
|
||||
* initialization.<br/>
|
||||
* As a rule of thumb, storing a view should never be an option.
|
||||
*
|
||||
* Raw views are incredibly fast and must be considered the best tool to
|
||||
* iterate components whenever knowing the entities to which they belong
|
||||
* isn't required.
|
||||
*
|
||||
* @sa view
|
||||
* @sa view<Entity, Component>
|
||||
* @sa persistent_view
|
||||
* @sa raw_view
|
||||
* @sa runtime_view
|
||||
*
|
||||
* @tparam Component Type of component used to construct the view.
|
||||
* @return A newly created raw view.
|
||||
*/
|
||||
template<typename Component>
|
||||
inline entt::raw_view<Entity, Component> raw_view() const {
|
||||
static_assert(std::is_const_v<Component>);
|
||||
return const_cast<registry *>(this)->raw_view<Component>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a runtime view for the given components.
|
||||
*
|
||||
@@ -1262,7 +1374,7 @@ public:
|
||||
* @return A newly created runtime view.
|
||||
*/
|
||||
template<typename It>
|
||||
entt::runtime_view<Entity> runtime_view(It first, It last) {
|
||||
entt::runtime_view<Entity> runtime_view(It first, It last) const {
|
||||
static_assert(std::is_convertible_v<typename std::iterator_traits<It>::value_type, component_type>);
|
||||
std::vector<const sparse_set<Entity> *> set(last - first);
|
||||
|
||||
@@ -1340,8 +1452,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<sparse_set<Entity>>> handlers;
|
||||
std::vector<std::unique_ptr<sparse_set<Entity>>> pools;
|
||||
mutable std::vector<std::unique_ptr<sparse_set<Entity>>> handlers;
|
||||
mutable std::vector<std::unique_ptr<sparse_set<Entity>>> pools;
|
||||
std::vector<entity_type> entities;
|
||||
size_type available{};
|
||||
entity_type next{};
|
||||
|
||||
@@ -76,20 +76,22 @@ class persistent_view final {
|
||||
friend class registry<Entity>;
|
||||
|
||||
template<typename Comp>
|
||||
using pool_type = sparse_set<Entity, Comp>;
|
||||
using pool_type = std::conditional_t<std::is_const_v<Comp>, const sparse_set<Entity, std::remove_const_t<Comp>>, sparse_set<Entity, Comp>>;
|
||||
|
||||
using view_type = sparse_set<Entity>;
|
||||
using persistent_type = sparse_set<Entity, std::array<typename view_type::size_type, sizeof...(Component)>>;
|
||||
using pattern_type = std::tuple<pool_type<Component> *...>;
|
||||
|
||||
persistent_view(persistent_type *handler, pool_type<Component> *... pools) ENTT_NOEXCEPT
|
||||
// we could use pool_type<Component> *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug)
|
||||
persistent_view(persistent_type *handler, sparse_set<Entity, std::remove_const_t<Component>> *... pools) ENTT_NOEXCEPT
|
||||
: handler{handler},
|
||||
pools{pools...}
|
||||
{}
|
||||
|
||||
template<typename Comp>
|
||||
const pool_type<Comp> * pool() const ENTT_NOEXCEPT {
|
||||
return std::get<pool_type<Comp> *>(pools);
|
||||
auto * pool() const ENTT_NOEXCEPT {
|
||||
using comp_type = std::conditional_t<std::disjunction_v<std::is_same<Comp, Component>...>, Comp, std::remove_const_t<Comp>>;
|
||||
return std::get<pool_type<comp_type> *>(pools);
|
||||
}
|
||||
|
||||
const view_type * candidate() const ENTT_NOEXCEPT {
|
||||
@@ -110,8 +112,9 @@ class persistent_view final {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
std::for_each(handler->view_type::cbegin(), handler->view_type::cend(), [&func, raw = handler->cbegin(), this](const auto entity) mutable {
|
||||
func(entity, pool<Component>()->raw()[(*raw)[Indexes]]...);
|
||||
std::for_each(handler->view_type::cbegin(), handler->view_type::cend(), [func = std::move(func), raw = handler->cbegin(), this](const auto entity) mutable {
|
||||
std::array<typename view_type::size_type, sizeof...(Indexes)> indexes{(*raw)[Indexes]...};
|
||||
func(entity, pool<Component>()->raw()[std::get<Indexes>(indexes)]...);
|
||||
++raw;
|
||||
});
|
||||
}
|
||||
@@ -123,9 +126,7 @@ public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename view_type::size_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = typename view_type::iterator_type;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = typename view_type::const_iterator_type;
|
||||
using iterator_type = typename view_type::const_iterator_type;
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
persistent_view(const persistent_view &) = default;
|
||||
@@ -183,46 +184,10 @@ public:
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
const_iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return handler->view_type::cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
inline const_iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
iterator_type begin() ENTT_NOEXCEPT {
|
||||
return handler->view_type::begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
@@ -238,48 +203,10 @@ public:
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
const_iterator_type cend() const ENTT_NOEXCEPT {
|
||||
iterator_type end() const ENTT_NOEXCEPT {
|
||||
return handler->view_type::cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
inline const_iterator_type end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
iterator_type end() ENTT_NOEXCEPT {
|
||||
return handler->view_type::end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the identifier that occupies the given position.
|
||||
* @param pos Position of the element to return.
|
||||
@@ -316,39 +243,13 @@ public:
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<typename... Comp>
|
||||
std::conditional_t<sizeof...(Comp) == 1, std::tuple_element_t<0, std::tuple<const Comp &...>>, std::tuple<const Comp &...>>
|
||||
std::conditional_t<sizeof...(Comp) == 1, std::tuple_element_t<0, std::tuple<Comp &...>>, std::tuple<Comp &...>>
|
||||
get([[maybe_unused]] const entity_type entity) const ENTT_NOEXCEPT {
|
||||
assert(contains(entity));
|
||||
|
||||
if constexpr(sizeof...(Comp) == 1) {
|
||||
static_assert(std::disjunction_v<std::is_same<Comp..., Component>..., std::is_same<std::remove_const_t<Comp>..., Component>...>);
|
||||
return (pool<Comp>()->get(entity), ...);
|
||||
} else {
|
||||
return std::tuple<const Comp &...>{get<Comp>(entity)...};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* Prefer this function instead of `registry::get` during iterations. It has
|
||||
* far better performance than its companion function.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an invalid component type results in a compilation
|
||||
* error. Attempting to use an entity that doesn't belong to the view
|
||||
* results in undefined behavior.<br/>
|
||||
* An assertion will abort the execution at runtime in debug mode if the
|
||||
* view doesn't contain the given entity.
|
||||
*
|
||||
* @tparam Comp Types of components to get.
|
||||
* @param entity A valid entity identifier.
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<typename... Comp>
|
||||
inline std::conditional_t<sizeof...(Comp) == 1, std::tuple_element_t<0, std::tuple<Comp &...>>, std::tuple<Comp &...>>
|
||||
get([[maybe_unused]] const entity_type entity) ENTT_NOEXCEPT {
|
||||
if constexpr(sizeof...(Comp) == 1) {
|
||||
return (const_cast<Comp &>(std::as_const(*this).template get<Comp>(entity)), ...);
|
||||
} else {
|
||||
return std::tuple<Comp &...>{get<Comp>(entity)...};
|
||||
}
|
||||
@@ -364,7 +265,7 @@ public:
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, const Component &...);
|
||||
* void(const entity_type, Component &...);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
@@ -375,29 +276,6 @@ public:
|
||||
each(std::move(func), std::make_index_sequence<sizeof...(Component)>{});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and components and applies the given function
|
||||
* object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided with the
|
||||
* entity itself and a set of references to all the components of the
|
||||
* view.<br/>
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, Component &...);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
inline void each(Func func) {
|
||||
each([&func](const entity_type entity, const Component &... component) {
|
||||
func(entity, const_cast<Component &>(component)...);
|
||||
}, std::make_index_sequence<sizeof...(Component)>{});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the internal data structures used by persistent views.
|
||||
*
|
||||
@@ -413,7 +291,7 @@ public:
|
||||
* one if in doubt. Initializing the view during an iteration allows to
|
||||
* considerably reduce the cost of this operation.
|
||||
*/
|
||||
void initialize() {
|
||||
void initialize() const {
|
||||
if(empty()) {
|
||||
const auto *view = candidate();
|
||||
|
||||
@@ -442,7 +320,7 @@ public:
|
||||
* @tparam Comp Type of component to use to impose the order.
|
||||
*/
|
||||
template<typename Comp>
|
||||
void sort() {
|
||||
void sort() const {
|
||||
handler->respect(*pool<Comp>());
|
||||
}
|
||||
|
||||
@@ -501,10 +379,10 @@ class view final {
|
||||
friend class registry<Entity>;
|
||||
|
||||
template<typename Comp>
|
||||
using pool_type = sparse_set<Entity, Comp>;
|
||||
using pool_type = std::conditional_t<std::is_const_v<Comp>, const sparse_set<Entity, std::remove_const_t<Comp>>, sparse_set<Entity, Comp>>;
|
||||
|
||||
template<typename Comp>
|
||||
using component_iterator_type = typename pool_type<Comp>::const_iterator_type;
|
||||
using component_iterator_type = decltype(std::declval<pool_type<Comp>>().begin());
|
||||
|
||||
using view_type = sparse_set<Entity>;
|
||||
using underlying_iterator_type = typename view_type::const_iterator_type;
|
||||
@@ -586,13 +464,15 @@ class view final {
|
||||
extent_type extent;
|
||||
};
|
||||
|
||||
view(pool_type<Component> *... pools) ENTT_NOEXCEPT
|
||||
// we could use pool_type<Component> *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug)
|
||||
view(sparse_set<Entity, std::remove_const_t<Component>> *... pools) ENTT_NOEXCEPT
|
||||
: pools{pools...}
|
||||
{}
|
||||
|
||||
template<typename Comp>
|
||||
const pool_type<Comp> * pool() const ENTT_NOEXCEPT {
|
||||
return std::get<pool_type<Comp> *>(pools);
|
||||
auto * pool() const ENTT_NOEXCEPT {
|
||||
using comp_type = std::conditional_t<std::disjunction_v<std::is_same<Comp, Component>...>, Comp, std::remove_const_t<Comp>>;
|
||||
return std::get<pool_type<comp_type> *>(pools);
|
||||
}
|
||||
|
||||
const view_type * candidate() const ENTT_NOEXCEPT {
|
||||
@@ -609,7 +489,7 @@ class view final {
|
||||
}
|
||||
|
||||
template<typename Comp, typename Other>
|
||||
inline const Other & get([[maybe_unused]] const component_iterator_type<Comp> &it, [[maybe_unused]] const Entity entity) const ENTT_NOEXCEPT {
|
||||
inline Other & get([[maybe_unused]] component_iterator_type<Comp> it, [[maybe_unused]] const Entity entity) const ENTT_NOEXCEPT {
|
||||
if constexpr(std::is_same_v<Comp, Other>) {
|
||||
return *it;
|
||||
} else {
|
||||
@@ -618,11 +498,11 @@ class view final {
|
||||
}
|
||||
|
||||
template<typename Comp, typename Func, std::size_t... Indexes>
|
||||
void each(const pool_type<Comp> *cpool, Func func, std::index_sequence<Indexes...>) const {
|
||||
void each(pool_type<Comp> *cpool, Func func, std::index_sequence<Indexes...>) const {
|
||||
const auto other = unchecked(cpool);
|
||||
std::array<underlying_iterator_type, sizeof...(Indexes)> data{{std::get<Indexes>(other)->cbegin()...}};
|
||||
const auto extent = std::min({ pool<Component>()->extent()... });
|
||||
auto raw = std::make_tuple(pool<Component>()->cbegin()...);
|
||||
auto raw = std::make_tuple(pool<Component>()->begin()...);
|
||||
const auto end = cpool->view_type::cend();
|
||||
auto begin = cpool->view_type::cbegin();
|
||||
|
||||
@@ -651,8 +531,6 @@ public:
|
||||
using size_type = typename view_type::size_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = iterator;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = iterator;
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
view(const view &) = default;
|
||||
@@ -694,45 +572,9 @@ public:
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
const_iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
iterator_type begin() const ENTT_NOEXCEPT {
|
||||
const auto *view = candidate();
|
||||
return const_iterator_type{unchecked(view), view->cbegin(), view->cend()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
inline const_iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
inline iterator_type begin() ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
return iterator_type{unchecked(view), view->cbegin(), view->cend()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -750,47 +592,9 @@ public:
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
const_iterator_type cend() const ENTT_NOEXCEPT {
|
||||
iterator_type end() const ENTT_NOEXCEPT {
|
||||
const auto *view = candidate();
|
||||
return const_iterator_type{unchecked(view), view->cend(), view->cend()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
inline const_iterator_type end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
inline iterator_type end() ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
return iterator_type{unchecked(view), view->cend(), view->cend()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -822,39 +626,13 @@ public:
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<typename... Comp>
|
||||
std::conditional_t<sizeof...(Comp) == 1, std::tuple_element_t<0, std::tuple<const Comp &...>>, std::tuple<const Comp &...>>
|
||||
std::conditional_t<sizeof...(Comp) == 1, std::tuple_element_t<0, std::tuple<Comp &...>>, std::tuple<Comp &...>>
|
||||
get([[maybe_unused]] const entity_type entity) const ENTT_NOEXCEPT {
|
||||
assert(contains(entity));
|
||||
|
||||
if constexpr(sizeof...(Comp) == 1) {
|
||||
static_assert(std::disjunction_v<std::is_same<Comp..., Component>..., std::is_same<std::remove_const_t<Comp>..., Component>...>);
|
||||
return (pool<Comp>()->get(entity), ...);
|
||||
} else {
|
||||
return std::tuple<const Comp &...>{get<Comp>(entity)...};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* Prefer this function instead of `registry::get` during iterations. It has
|
||||
* far better performance than its companion function.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an invalid component type results in a compilation
|
||||
* error. Attempting to use an entity that doesn't belong to the view
|
||||
* results in undefined behavior.<br/>
|
||||
* An assertion will abort the execution at runtime in debug mode if the
|
||||
* view doesn't contain the given entity.
|
||||
*
|
||||
* @tparam Comp Types of components to get.
|
||||
* @param entity A valid entity identifier.
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<typename... Comp>
|
||||
inline std::conditional_t<sizeof...(Comp) == 1, std::tuple_element_t<0, std::tuple<Comp &...>>, std::tuple<Comp &...>>
|
||||
get([[maybe_unused]] const entity_type entity) ENTT_NOEXCEPT {
|
||||
if constexpr(sizeof...(Comp) == 1) {
|
||||
return (const_cast<Comp &>(std::as_const(*this).template get<Comp>(entity)), ...);
|
||||
} else {
|
||||
return std::tuple<Comp &...>{get<Comp>(entity)...};
|
||||
}
|
||||
@@ -870,7 +648,7 @@ public:
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, const Component &...);
|
||||
* void(const entity_type, Component &...);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
@@ -879,30 +657,7 @@ public:
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
const auto *view = candidate();
|
||||
((pool<Component>() == view ? each(pool<Component>(), std::move(func), std::make_index_sequence<sizeof...(Component)-1>{}) : void()), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and components and applies the given function
|
||||
* object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided with the
|
||||
* entity itself and a set of references to all the components of the
|
||||
* view.<br/>
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, Component &...);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
inline void each(Func func) {
|
||||
std::as_const(*this).each([&func](const entity_type entity, const Component &... component) {
|
||||
func(entity, const_cast<Component &>(component)...);
|
||||
});
|
||||
((pool<Component>() == view ? each<Component>(pool<Component>(), std::move(func), std::make_index_sequence<sizeof...(Component)-1>{}) : void()), ...);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -954,7 +709,7 @@ class view<Entity, Component> final {
|
||||
friend class registry<Entity>;
|
||||
|
||||
using view_type = sparse_set<Entity>;
|
||||
using pool_type = sparse_set<Entity, Component>;
|
||||
using pool_type = std::conditional_t<std::is_const_v<Component>, const sparse_set<Entity, std::remove_const_t<Component>>, sparse_set<Entity, Component>>;
|
||||
|
||||
view(pool_type *pool) ENTT_NOEXCEPT
|
||||
: pool{pool}
|
||||
@@ -962,15 +717,13 @@ class view<Entity, Component> final {
|
||||
|
||||
public:
|
||||
/*! @brief Type of component iterated by the view. */
|
||||
using raw_type = typename pool_type::object_type;
|
||||
using raw_type = std::remove_reference_t<decltype(std::declval<pool_type>().get(0))>;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename pool_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename pool_type::size_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = typename view_type::iterator_type;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = typename view_type::const_iterator_type;
|
||||
using iterator_type = typename view_type::const_iterator_type;
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
view(const view &) = default;
|
||||
@@ -1010,26 +763,10 @@ public:
|
||||
*
|
||||
* @return A pointer to the array of components.
|
||||
*/
|
||||
const raw_type * raw() const ENTT_NOEXCEPT {
|
||||
raw_type * raw() const ENTT_NOEXCEPT {
|
||||
return pool->raw();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the list of components.
|
||||
*
|
||||
* The returned pointer is such that range `[raw(), raw() + size()]` is
|
||||
* always a valid range, even if the container is empty.
|
||||
*
|
||||
* @note
|
||||
* There are no guarantees on the order of the components. Use `begin` and
|
||||
* `end` if you want to iterate the view in the expected order.
|
||||
*
|
||||
* @return A pointer to the array of components.
|
||||
*/
|
||||
inline raw_type * raw() ENTT_NOEXCEPT {
|
||||
return const_cast<raw_type *>(std::as_const(*this).raw());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the list of entities.
|
||||
*
|
||||
@@ -1060,46 +797,10 @@ public:
|
||||
*
|
||||
* @return An iterator to the first entity that has the given component.
|
||||
*/
|
||||
const_iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return pool->view_type::cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* component.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* component. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given component.
|
||||
*/
|
||||
inline const_iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* component.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* component. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given component.
|
||||
*/
|
||||
iterator_type begin() ENTT_NOEXCEPT {
|
||||
return pool->view_type::begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given component.
|
||||
@@ -1115,48 +816,10 @@ public:
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given component.
|
||||
*/
|
||||
const_iterator_type cend() const ENTT_NOEXCEPT {
|
||||
iterator_type end() const ENTT_NOEXCEPT {
|
||||
return pool->view_type::cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given component.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given component. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given component.
|
||||
*/
|
||||
inline const_iterator_type end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given component.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given component. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given component.
|
||||
*/
|
||||
iterator_type end() ENTT_NOEXCEPT {
|
||||
return pool->view_type::end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the identifier that occupies the given position.
|
||||
* @param pos Position of the element to return.
|
||||
@@ -1190,30 +853,11 @@ public:
|
||||
* @param entity A valid entity identifier.
|
||||
* @return The component assigned to the entity.
|
||||
*/
|
||||
const Component & get(const entity_type entity) const ENTT_NOEXCEPT {
|
||||
raw_type & get(const entity_type entity) const ENTT_NOEXCEPT {
|
||||
assert(contains(entity));
|
||||
return pool->get(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the component assigned to the given entity.
|
||||
*
|
||||
* Prefer this function instead of `registry::get` during iterations. It has
|
||||
* far better performance than its companion function.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the view results in
|
||||
* undefined behavior.<br/>
|
||||
* An assertion will abort the execution at runtime in debug mode if the
|
||||
* view doesn't contain the given entity.
|
||||
*
|
||||
* @param entity A valid entity identifier.
|
||||
* @return The component assigned to the entity.
|
||||
*/
|
||||
inline Component & get(const entity_type entity) ENTT_NOEXCEPT {
|
||||
return const_cast<Component &>(std::as_const(*this).get(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and components and applies the given function
|
||||
* object to them.
|
||||
@@ -1231,33 +875,11 @@ public:
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
std::for_each(pool->view_type::cbegin(), pool->view_type::cend(), [&func, raw = pool->cbegin()](const auto entity) mutable {
|
||||
std::for_each(pool->view_type::cbegin(), pool->view_type::cend(), [func = std::move(func), raw = pool->begin()](const auto entity) mutable {
|
||||
func(entity, *(raw++));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and components and applies the given function
|
||||
* object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided with the
|
||||
* entity itself and a reference to the component of the view.<br/>
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, Component &);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
inline void each(Func func) {
|
||||
std::as_const(*this).each([&func](const entity_type entity, const Component &component) {
|
||||
func(entity, const_cast<Component &>(component));
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
pool_type *pool;
|
||||
};
|
||||
@@ -1307,7 +929,7 @@ class raw_view final {
|
||||
/*! @brief A registry is allowed to create views. */
|
||||
friend class registry<Entity>;
|
||||
|
||||
using pool_type = sparse_set<Entity, Component>;
|
||||
using pool_type = std::conditional_t<std::is_const_v<Component>, const sparse_set<Entity, std::remove_const_t<Component>>, sparse_set<Entity, Component>>;
|
||||
|
||||
raw_view(pool_type *pool) ENTT_NOEXCEPT
|
||||
: pool{pool}
|
||||
@@ -1315,15 +937,13 @@ class raw_view final {
|
||||
|
||||
public:
|
||||
/*! @brief Type of component iterated by the view. */
|
||||
using raw_type = typename pool_type::object_type;
|
||||
using raw_type = std::remove_reference_t<decltype(std::declval<pool_type>().get(0))>;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename pool_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename pool_type::size_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = typename pool_type::iterator_type;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = typename pool_type::const_iterator_type;
|
||||
using iterator_type = decltype(std::declval<pool_type>().begin());
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
raw_view(const raw_view &) = default;
|
||||
@@ -1363,26 +983,10 @@ public:
|
||||
*
|
||||
* @return A pointer to the array of components.
|
||||
*/
|
||||
const raw_type * raw() const ENTT_NOEXCEPT {
|
||||
raw_type * raw() const ENTT_NOEXCEPT {
|
||||
return pool->raw();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the list of components.
|
||||
*
|
||||
* The returned pointer is such that range `[raw(), raw() + size()]` is
|
||||
* always a valid range, even if the container is empty.
|
||||
*
|
||||
* @note
|
||||
* There are no guarantees on the order of the components. Use `begin` and
|
||||
* `end` if you want to iterate the view in the expected order.
|
||||
*
|
||||
* @return A pointer to the array of components.
|
||||
*/
|
||||
inline raw_type * raw() ENTT_NOEXCEPT {
|
||||
return const_cast<raw_type *>(std::as_const(*this).raw());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the list of entities.
|
||||
*
|
||||
@@ -1411,39 +1015,7 @@ public:
|
||||
*
|
||||
* @return An iterator to the first instance of the given type.
|
||||
*/
|
||||
const_iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
return pool->cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first instance of the given type.
|
||||
*
|
||||
* The returned iterator points to the first instance of the given type. If
|
||||
* the view is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first instance of the given type.
|
||||
*/
|
||||
inline const_iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first instance of the given type.
|
||||
*
|
||||
* The returned iterator points to the first instance of the given type. If
|
||||
* the view is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first instance of the given type.
|
||||
*/
|
||||
iterator_type begin() ENTT_NOEXCEPT {
|
||||
iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return pool->begin();
|
||||
}
|
||||
|
||||
@@ -1462,45 +1034,7 @@ public:
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* given type.
|
||||
*/
|
||||
const_iterator_type cend() const ENTT_NOEXCEPT {
|
||||
return pool->cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last instance of the given
|
||||
* type.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the given type. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* given type.
|
||||
*/
|
||||
inline const_iterator_type end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last instance of the given
|
||||
* type.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the given type. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* given type.
|
||||
*/
|
||||
iterator_type end() ENTT_NOEXCEPT {
|
||||
iterator_type end() const ENTT_NOEXCEPT {
|
||||
return pool->end();
|
||||
}
|
||||
|
||||
@@ -1509,36 +1043,8 @@ public:
|
||||
* @param pos Position of the element to return.
|
||||
* @return A reference to the requested element.
|
||||
*/
|
||||
const raw_type & operator[](const size_type pos) const ENTT_NOEXCEPT {
|
||||
return pool->cbegin()[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the element at the given position.
|
||||
* @param pos Position of the element to return.
|
||||
* @return A reference to the requested element.
|
||||
*/
|
||||
inline raw_type & operator[](const size_type pos) ENTT_NOEXCEPT {
|
||||
return const_cast<raw_type &>(std::as_const(*this).operator[](pos));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates components and applies the given function object to them.
|
||||
*
|
||||
* The function object is provided with a const reference to each component
|
||||
* of the view.<br/>
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const Component &);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
std::for_each(pool->cbegin(), pool->cend(), func);
|
||||
raw_type & operator[](const size_type pos) const ENTT_NOEXCEPT {
|
||||
return pool->begin()[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1556,8 +1062,8 @@ public:
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) {
|
||||
std::for_each(pool->begin(), pool->end(), func);
|
||||
void each(Func func) const {
|
||||
std::for_each(pool->begin(), pool->end(), std::move(func));
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1723,8 +1229,6 @@ public:
|
||||
using size_type = typename view_type::size_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = iterator;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = iterator;
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
runtime_view(const runtime_view &) = default;
|
||||
@@ -1766,8 +1270,8 @@ public:
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
const_iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
const_iterator_type it{};
|
||||
iterator_type begin() const ENTT_NOEXCEPT {
|
||||
iterator_type it{};
|
||||
|
||||
if(valid()) {
|
||||
const auto &pool = *pools.front();
|
||||
@@ -1778,42 +1282,6 @@ public:
|
||||
return it;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
inline const_iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
inline iterator_type begin() ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
@@ -1829,8 +1297,8 @@ public:
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
const_iterator_type cend() const ENTT_NOEXCEPT {
|
||||
const_iterator_type it{};
|
||||
iterator_type end() const ENTT_NOEXCEPT {
|
||||
iterator_type it{};
|
||||
|
||||
if(valid()) {
|
||||
const auto &pool = *pools.front();
|
||||
@@ -1840,44 +1308,6 @@ public:
|
||||
return it;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
inline const_iterator_type end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
inline iterator_type end() ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a view contains an entity.
|
||||
* @param entity A valid entity identifier.
|
||||
@@ -1906,7 +1336,7 @@ public:
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
std::for_each(cbegin(), cend(), func);
|
||||
std::for_each(begin(), end(), func);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -225,7 +225,7 @@ TEST(Snapshot, Iterator) {
|
||||
const auto view = registry.view<a_component>();
|
||||
const auto size = view.size();
|
||||
|
||||
registry.snapshot().component<another_component>(output, view.cbegin(), view.cend());
|
||||
registry.snapshot().component<another_component>(output, view.begin(), view.end());
|
||||
registry.reset();
|
||||
registry.loader().component<another_component>(input);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
TEST(PersistentView, Functionalities) {
|
||||
entt::registry<> registry;
|
||||
auto view = registry.persistent_view<int, char>();
|
||||
const auto &cview = view;
|
||||
auto cview = std::as_const(registry).persistent_view<const int, const char>();
|
||||
|
||||
ASSERT_TRUE(view.empty());
|
||||
|
||||
@@ -19,8 +19,8 @@ TEST(PersistentView, Functionalities) {
|
||||
registry.assign<char>(e1);
|
||||
|
||||
ASSERT_FALSE(view.empty());
|
||||
ASSERT_NO_THROW((registry.persistent_view<int, char>().begin()++));
|
||||
ASSERT_NO_THROW((++registry.persistent_view<int, char>().begin()));
|
||||
ASSERT_NO_THROW((view.begin()++));
|
||||
ASSERT_NO_THROW((++cview.begin()));
|
||||
|
||||
ASSERT_NE(view.begin(), view.end());
|
||||
ASSERT_NE(cview.begin(), cview.end());
|
||||
@@ -39,10 +39,9 @@ TEST(PersistentView, Functionalities) {
|
||||
registry.get<int>(e1) = 42;
|
||||
|
||||
for(auto entity: view) {
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
ASSERT_EQ(std::get<0>(cview.get<int, char>(entity)), 42);
|
||||
ASSERT_EQ(std::get<0>(cview.get<const int, const char>(entity)), 42);
|
||||
ASSERT_EQ(std::get<1>(view.get<int, char>(entity)), '2');
|
||||
ASSERT_EQ(cview.get<char>(entity), '2');
|
||||
ASSERT_EQ(cview.get<const char>(entity), '2');
|
||||
}
|
||||
|
||||
ASSERT_EQ(*(view.data() + 0), e1);
|
||||
@@ -51,14 +50,14 @@ TEST(PersistentView, Functionalities) {
|
||||
registry.remove<char>(e1);
|
||||
|
||||
ASSERT_EQ(view.begin(), view.end());
|
||||
ASSERT_EQ(view.cbegin(), view.cend());
|
||||
ASSERT_EQ(cview.begin(), cview.end());
|
||||
ASSERT_TRUE(view.empty());
|
||||
}
|
||||
|
||||
TEST(PersistentView, ElementAccess) {
|
||||
entt::registry<> registry;
|
||||
auto view = registry.persistent_view<int, char>();
|
||||
const auto &cview = view;
|
||||
auto cview = std::as_const(registry).persistent_view<const int, const char>();
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
@@ -127,7 +126,7 @@ TEST(PersistentView, Each) {
|
||||
registry.assign<int>(e1);
|
||||
registry.assign<char>(e1);
|
||||
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
auto cview = std::as_const(registry).persistent_view<const int, const char>();
|
||||
std::size_t cnt = 0;
|
||||
|
||||
view.each([&cnt](auto, int &, char &) { ++cnt; });
|
||||
@@ -141,7 +140,7 @@ TEST(PersistentView, Each) {
|
||||
|
||||
TEST(PersistentView, Sort) {
|
||||
entt::registry<> registry;
|
||||
auto view = registry.persistent_view<int, unsigned int>();
|
||||
auto view = registry.persistent_view<const int, unsigned int>();
|
||||
|
||||
const auto e0 = registry.create();
|
||||
const auto e1 = registry.create();
|
||||
@@ -160,7 +159,7 @@ TEST(PersistentView, Sort) {
|
||||
|
||||
for(auto entity: view) {
|
||||
ASSERT_EQ(view.get<unsigned int>(entity), --uval);
|
||||
ASSERT_EQ(view.get<int>(entity), --ival);
|
||||
ASSERT_EQ(view.get<const int>(entity), --ival);
|
||||
}
|
||||
|
||||
registry.sort<unsigned int>(std::less<unsigned int>{});
|
||||
@@ -168,7 +167,7 @@ TEST(PersistentView, Sort) {
|
||||
|
||||
for(auto entity: view) {
|
||||
ASSERT_EQ(view.get<unsigned int>(entity), uval++);
|
||||
ASSERT_EQ(view.get<int>(entity), ival++);
|
||||
ASSERT_EQ(view.get<const int>(entity), ival++);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +182,7 @@ TEST(PersistentView, Initialize) {
|
||||
|
||||
registry.assign<int>(registry.create());
|
||||
|
||||
auto view = registry.persistent_view<int, char>();
|
||||
auto view = std::as_const(registry).persistent_view<const int, const char>();
|
||||
|
||||
ASSERT_TRUE(view.empty());
|
||||
ASSERT_EQ(view.size(), typename decltype(view)::size_type{});
|
||||
@@ -322,10 +321,26 @@ TEST(PersistentView, IndexRebuiltOnDestroy) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST(PersistentView, ConstNonConstAndAllInBetween) {
|
||||
entt::registry<> registry;
|
||||
auto view = registry.persistent_view<int, const char>();
|
||||
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.get<int>(0)), int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.get<const int>(0)), const int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.get<const char>(0)), const char &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.get<int, const char>(0)), std::tuple<int &, const char &>>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.get<const int, const char>(0)), std::tuple<const int &, const char &>>));
|
||||
|
||||
view.each([](auto, auto &&i, auto &&c) {
|
||||
ASSERT_TRUE((std::is_same_v<decltype(i), int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(c), const char &>));
|
||||
});
|
||||
}
|
||||
|
||||
TEST(SingleComponentView, Functionalities) {
|
||||
entt::registry<> registry;
|
||||
auto view = registry.view<char>();
|
||||
const auto &cview = view;
|
||||
auto cview = std::as_const(registry).view<const char>();
|
||||
|
||||
const auto e0 = registry.create();
|
||||
const auto e1 = registry.create();
|
||||
@@ -351,7 +366,6 @@ TEST(SingleComponentView, Functionalities) {
|
||||
view.get(e1) = '2';
|
||||
|
||||
for(auto entity: view) {
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
ASSERT_TRUE(cview.get(entity) == '1' || cview.get(entity) == '2');
|
||||
}
|
||||
|
||||
@@ -365,14 +379,13 @@ TEST(SingleComponentView, Functionalities) {
|
||||
registry.remove<char>(e1);
|
||||
|
||||
ASSERT_EQ(view.begin(), view.end());
|
||||
ASSERT_EQ(view.cbegin(), view.cend());
|
||||
ASSERT_TRUE(view.empty());
|
||||
}
|
||||
|
||||
TEST(SingleComponentView, ElementAccess) {
|
||||
entt::registry<> registry;
|
||||
auto view = registry.view<int>();
|
||||
const auto &cview = view;
|
||||
auto cview = std::as_const(registry).view<const int>();
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0);
|
||||
@@ -442,10 +455,32 @@ TEST(SingleComponentView, Each) {
|
||||
ASSERT_EQ(cnt, std::size_t{0});
|
||||
}
|
||||
|
||||
TEST(SingleComponentView, ConstNonConstAndAllInBetween) {
|
||||
entt::registry<> registry;
|
||||
auto view = registry.view<int>();
|
||||
auto cview = registry.view<const int>();
|
||||
|
||||
ASSERT_TRUE((std::is_same_v<typename decltype(view)::raw_type, int>));
|
||||
ASSERT_TRUE((std::is_same_v<typename decltype(cview)::raw_type, const int>));
|
||||
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.get(0)), int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.raw()), int *>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(cview.get(0)), const int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(cview.raw()), const int *>));
|
||||
|
||||
view.each([](auto, auto &&i) {
|
||||
ASSERT_TRUE((std::is_same_v<decltype(i), int &>));
|
||||
});
|
||||
|
||||
cview.each([](auto, auto &&i) {
|
||||
ASSERT_TRUE((std::is_same_v<decltype(i), const int &>));
|
||||
});
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, Functionalities) {
|
||||
entt::registry<> registry;
|
||||
auto view = registry.view<int, char>();
|
||||
const auto &cview = view;
|
||||
auto cview = std::as_const(registry).view<const int, const char>();
|
||||
|
||||
ASSERT_TRUE(view.empty());
|
||||
|
||||
@@ -476,10 +511,9 @@ TEST(MultipleComponentView, Functionalities) {
|
||||
registry.get<int>(e1) = 42;
|
||||
|
||||
for(auto entity: view) {
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
ASSERT_EQ(std::get<0>(cview.get<int, char>(entity)), 42);
|
||||
ASSERT_EQ(std::get<0>(cview.get<const int, const char>(entity)), 42);
|
||||
ASSERT_EQ(std::get<1>(view.get<int, char>(entity)), '2');
|
||||
ASSERT_EQ(cview.get<char>(entity), '2');
|
||||
ASSERT_EQ(cview.get<const char>(entity), '2');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,28 +539,6 @@ TEST(MultipleComponentView, Iterator) {
|
||||
ASSERT_EQ(++view.begin(), view.end());
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, ConstIterator) {
|
||||
entt::registry<> registry;
|
||||
const auto entity = registry.create();
|
||||
registry.assign<int>(entity);
|
||||
registry.assign<char>(entity);
|
||||
|
||||
const auto view = registry.view<int, char>();
|
||||
using iterator_type = typename decltype(view)::iterator_type;
|
||||
|
||||
iterator_type cend{view.cbegin()};
|
||||
iterator_type cbegin{};
|
||||
cbegin = view.cend();
|
||||
std::swap(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(cbegin, view.cbegin());
|
||||
ASSERT_EQ(cend, view.cend());
|
||||
ASSERT_NE(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(view.cbegin()++, view.cbegin());
|
||||
ASSERT_EQ(++view.cbegin(), view.cend());
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, Contains) {
|
||||
entt::registry<> registry;
|
||||
|
||||
@@ -578,7 +590,7 @@ TEST(MultipleComponentView, Each) {
|
||||
registry.assign<char>(e1);
|
||||
|
||||
auto view = registry.view<int, char>();
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
auto cview = std::as_const(registry).view<const int, const char>();
|
||||
std::size_t cnt = 0;
|
||||
|
||||
view.each([&cnt](auto, int &, char &) { ++cnt; });
|
||||
@@ -615,10 +627,26 @@ TEST(MultipleComponentView, EachWithHoles) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST(MultipleComponentView, ConstNonConstAndAllInBetween) {
|
||||
entt::registry<> registry;
|
||||
auto view = registry.view<int, const char>();
|
||||
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.get<int>(0)), int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.get<const int>(0)), const int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.get<const char>(0)), const char &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.get<int, const char>(0)), std::tuple<int &, const char &>>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.get<const int, const char>(0)), std::tuple<const int &, const char &>>));
|
||||
|
||||
view.each([](auto, auto &&i, auto &&c) {
|
||||
ASSERT_TRUE((std::is_same_v<decltype(i), int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(c), const char &>));
|
||||
});
|
||||
}
|
||||
|
||||
TEST(RawView, Functionalities) {
|
||||
entt::registry<> registry;
|
||||
auto view = registry.raw_view<char>();
|
||||
const auto &cview = view;
|
||||
auto cview = std::as_const(registry).raw_view<const char>();
|
||||
|
||||
ASSERT_TRUE(view.empty());
|
||||
|
||||
@@ -629,8 +657,8 @@ TEST(RawView, Functionalities) {
|
||||
registry.assign<char>(e1);
|
||||
|
||||
ASSERT_FALSE(view.empty());
|
||||
ASSERT_NO_THROW(registry.raw_view<char>().begin()++);
|
||||
ASSERT_NO_THROW(++registry.raw_view<char>().begin());
|
||||
ASSERT_NO_THROW(view.begin()++);
|
||||
ASSERT_NO_THROW(++cview.begin());
|
||||
|
||||
ASSERT_NE(view.begin(), view.end());
|
||||
ASSERT_NE(cview.begin(), cview.end());
|
||||
@@ -651,14 +679,14 @@ TEST(RawView, Functionalities) {
|
||||
ASSERT_EQ(*(view.data() + 1), e0);
|
||||
|
||||
ASSERT_EQ(*(view.raw() + 0), '2');
|
||||
ASSERT_EQ(*(static_cast<const decltype(view) &>(view).raw() + 1), '1');
|
||||
ASSERT_EQ(*(cview.raw() + 1), '1');
|
||||
|
||||
for(auto &&component: view) {
|
||||
// verifies that iterators return references to components
|
||||
component = '0';
|
||||
}
|
||||
|
||||
for(auto &&component: view) {
|
||||
for(auto &&component: cview) {
|
||||
ASSERT_TRUE(component == '0');
|
||||
}
|
||||
|
||||
@@ -666,14 +694,13 @@ TEST(RawView, Functionalities) {
|
||||
registry.remove<char>(e1);
|
||||
|
||||
ASSERT_EQ(view.begin(), view.end());
|
||||
ASSERT_EQ(view.cbegin(), view.cend());
|
||||
ASSERT_TRUE(view.empty());
|
||||
}
|
||||
|
||||
TEST(RawView, ElementAccess) {
|
||||
entt::registry<> registry;
|
||||
auto view = registry.raw_view<int>();
|
||||
const auto &cview = view;
|
||||
auto cview = std::as_const(registry).raw_view<const int>();
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0, 42);
|
||||
@@ -714,7 +741,7 @@ TEST(RawView, Each) {
|
||||
registry.assign<int>(registry.create(), 3);
|
||||
|
||||
auto view = registry.raw_view<int>();
|
||||
const auto &cview = static_cast<const decltype(view) &>(view);
|
||||
auto cview = std::as_const(registry).raw_view<const int>();
|
||||
std::size_t cnt = 0;
|
||||
|
||||
view.each([&cnt](int &v) { cnt += (v % 2); });
|
||||
@@ -726,6 +753,36 @@ TEST(RawView, Each) {
|
||||
ASSERT_EQ(cnt, std::size_t{0});
|
||||
}
|
||||
|
||||
TEST(RawView, ConstNonConstAndAllInBetween) {
|
||||
entt::registry<> registry;
|
||||
auto view = registry.raw_view<int>();
|
||||
auto cview = registry.raw_view<const int>();
|
||||
|
||||
ASSERT_TRUE((std::is_same_v<typename decltype(view)::raw_type, int>));
|
||||
ASSERT_TRUE((std::is_same_v<typename decltype(cview)::raw_type, const int>));
|
||||
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view[0]), int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.raw()), int *>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(cview[0]), const int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(cview.raw()), const int *>));
|
||||
|
||||
view.each([](auto &&i) {
|
||||
ASSERT_TRUE((std::is_same_v<decltype(i), int &>));
|
||||
});
|
||||
|
||||
cview.each([](auto &&i) {
|
||||
ASSERT_TRUE((std::is_same_v<decltype(i), const int &>));
|
||||
});
|
||||
|
||||
for(auto &&i: view) {
|
||||
ASSERT_TRUE((std::is_same_v<decltype(i), int &>));
|
||||
}
|
||||
|
||||
for(auto &&i: cview) {
|
||||
ASSERT_TRUE((std::is_same_v<decltype(i), const int &>));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RuntimeView, Functionalities) {
|
||||
entt::registry<> registry;
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
@@ -736,7 +793,6 @@ TEST(RuntimeView, Functionalities) {
|
||||
|
||||
component_type types[] = { registry.type<int>(), registry.type<char>() };
|
||||
auto view = registry.runtime_view(std::begin(types), std::end(types));
|
||||
const auto &cview = view;
|
||||
|
||||
ASSERT_TRUE(view.empty());
|
||||
|
||||
@@ -759,7 +815,6 @@ TEST(RuntimeView, Functionalities) {
|
||||
ASSERT_NO_THROW((++registry.runtime_view(std::begin(types), std::end(types)).begin()));
|
||||
|
||||
ASSERT_NE(view.begin(), view.end());
|
||||
ASSERT_NE(cview.begin(), cview.end());
|
||||
ASSERT_EQ(view.size(), decltype(view.size()){1});
|
||||
|
||||
registry.get<char>(e0) = '1';
|
||||
@@ -797,31 +852,6 @@ TEST(RuntimeView, Iterator) {
|
||||
ASSERT_EQ(++view.begin(), view.end());
|
||||
}
|
||||
|
||||
TEST(RuntimeView, ConstIterator) {
|
||||
entt::registry<> registry;
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
|
||||
const auto entity = registry.create();
|
||||
registry.assign<int>(entity);
|
||||
registry.assign<char>(entity);
|
||||
|
||||
component_type types[] = { registry.type<int>(), registry.type<char>() };
|
||||
auto view = registry.runtime_view(std::begin(types), std::end(types));
|
||||
using iterator_type = typename decltype(view)::iterator_type;
|
||||
|
||||
iterator_type cend{view.cbegin()};
|
||||
iterator_type cbegin{};
|
||||
cbegin = view.cend();
|
||||
std::swap(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(cbegin, view.cbegin());
|
||||
ASSERT_EQ(cend, view.cend());
|
||||
ASSERT_NE(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(view.cbegin()++, view.cbegin());
|
||||
ASSERT_EQ(++view.cbegin(), view.cend());
|
||||
}
|
||||
|
||||
TEST(RuntimeView, Contains) {
|
||||
entt::registry<> registry;
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
|
||||
Reference in New Issue
Block a user