review: instance-less pools for empty types
This commit is contained in:
3
TODO
3
TODO
@@ -22,4 +22,7 @@
|
||||
* early out in views using bitmasks with bloom filter like access based on modulus
|
||||
- standard each, use bitmask to speed up the whole thing and avoid accessing the pools to test for the page
|
||||
- iterator based each with a couple of iterators passed from outside (use bitmask + has)
|
||||
* multi component registry::remove and some others?
|
||||
* reactive systems
|
||||
|
||||
* update doc + inline doc (instance-less pools for empty types)
|
||||
|
||||
@@ -1460,25 +1460,21 @@ groups or as free types with multi component views and groups in general.
|
||||
An empty type `T` is such that `std::is_empty_v<T>` returns true. They are also
|
||||
the same types for which _empty base optimization_ (EBO) is possibile.<br/>
|
||||
`EnTT` handles these types in a special way, optimizing both in terms of
|
||||
performance and memory usage. However, this also has drawbacks that are worth
|
||||
performance and memory usage. However, this also has consequences that are worth
|
||||
mentioning.
|
||||
|
||||
When an empty type is detected, a pool is created in such a way that one and
|
||||
only one instance of the given type is created. All the entities will refer to
|
||||
it. Since the type is, in fact, empty, this is safe and there is no risk that a
|
||||
modification of its data members will affect other instances.<br/>
|
||||
Iterations are faster because only the entities to which the type is assigned
|
||||
are considered. Similarly, less memory is used, since there exists always only
|
||||
one instance of the component itself, no matter how many entities it is
|
||||
assigned.
|
||||
When an empty type is detected, it's not instantiated in any case. Therefore,
|
||||
only the entities to which it's assigned are made availble. All the iterators as
|
||||
well as the `get` member functions of registries, views and groups will return
|
||||
temporary objects. Similarly, some functions such as `try_get` or the raw access
|
||||
to the list of components aren't available for this kind of types.<br/>
|
||||
On the other hand, iterations are faster because only the entities to which the
|
||||
type is assigned are considered. Moreover, less memory is used, since there
|
||||
doesn't exist any instance of the component, no matter how many entities it is
|
||||
assigned to.
|
||||
|
||||
The drawback is that the `raw` member function will no longer be able to return
|
||||
a valid pointer to the list of components in the pool. This is because there is
|
||||
no list of components at all. Only one instance of the given type exists in this
|
||||
case. Therefore, `raw` will always return a pointer to that instance.<br/>
|
||||
Nonetheless, the iterators returned by the `begin` and `end` member functions
|
||||
are still valid and can be used safely. More in general, all the features
|
||||
offered by the library aren't affected, but for the `raw` member function.
|
||||
More in general, none of the features offered by the library is affected, but
|
||||
for the ones that require to return actual instances.
|
||||
|
||||
# Multithreading
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ struct basic_actor {
|
||||
* @return A reference to the newly created component.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
Component & assign(Args &&... args) {
|
||||
decltype(auto) assign(Args &&... args) {
|
||||
return reg->template assign_or_replace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
@@ -164,11 +164,6 @@ public:
|
||||
* There are no guarantees on the order of the components. Use `begin` and
|
||||
* `end` if you want to iterate the group in the expected order.
|
||||
*
|
||||
* @warning
|
||||
* Empty components aren't explicitly instantiated. Only one instance of the
|
||||
* given type is created. Therefore, this function always returns a pointer
|
||||
* to that instance.
|
||||
*
|
||||
* @tparam Component Type of component in which one is interested.
|
||||
* @return A pointer to the array of components.
|
||||
*/
|
||||
@@ -296,14 +291,13 @@ public:
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<typename... Component>
|
||||
std::conditional_t<sizeof...(Component) == 1, std::tuple_element_t<0, std::tuple<Component &...>>, std::tuple<Component &...>>
|
||||
get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
||||
decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(contains(entt));
|
||||
|
||||
if constexpr(sizeof...(Component) == 1) {
|
||||
return (std::get<pool_type<Component> *>(pools)->get(entt), ...);
|
||||
} else {
|
||||
return std::tuple<Component &...>{get<Component>(entt)...};
|
||||
return std::tuple<decltype(get<Component>(entt))...>{get<Component>(entt)...};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,13 +316,18 @@ public:
|
||||
* void(Get &...);
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated. Therefore, temporary objects
|
||||
* are returned during iterations. They can be caught only by copy or with
|
||||
* const references.
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
inline void each(Func func) const {
|
||||
for(const auto entt: *handler) {
|
||||
if constexpr(std::is_invocable_v<Func, std::add_lvalue_reference_t<Get>...>) {
|
||||
if constexpr(std::is_invocable_v<Func, decltype(get<Get>({}))...>) {
|
||||
func(std::get<pool_type<Get> *>(pools)->get(entt)...);
|
||||
} else {
|
||||
func(entt, std::get<pool_type<Get> *>(pools)->get(entt)...);
|
||||
@@ -422,14 +421,30 @@ class basic_group<Entity, get_t<Get...>, Owned...> {
|
||||
using component_iterator_type = decltype(std::declval<pool_type<Component>>().begin());
|
||||
|
||||
template<typename Component>
|
||||
const Component & from_index(const typename sparse_set<Entity>::size_type index) {
|
||||
decltype(auto) from_index(const typename sparse_set<Entity>::size_type index) {
|
||||
if constexpr(std::disjunction_v<std::is_same<Component, Owned>...>) {
|
||||
return std::get<pool_type<Component> *>(pools)->raw()[index];
|
||||
if constexpr(std::is_empty_v<Component>) {
|
||||
return std::get<pool_type<Component> *>(pools).get();
|
||||
} else {
|
||||
return std::as_const(*std::get<pool_type<Component> *>(pools)).raw()[index];
|
||||
}
|
||||
} else {
|
||||
return std::get<pool_type<Component> *>(pools)->get(data()[index]);
|
||||
return std::as_const(*std::get<pool_type<Component> *>(pools)).get(data()[index]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
inline auto swap(int, pool_type<Component> *cpool, const std::size_t lhs, const std::size_t rhs)
|
||||
-> decltype(cpool->raw(), void()) {
|
||||
std::swap(cpool->raw()[lhs], cpool->raw()[rhs]);
|
||||
cpool->swap(lhs, rhs);
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
inline void swap(char, pool_type<Component> *cpool, const std::size_t lhs, const std::size_t rhs) {
|
||||
cpool->swap(lhs, rhs);
|
||||
}
|
||||
|
||||
// we could use pool_type<Type> *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug)
|
||||
basic_group(const typename basic_registry<Entity>::size_type *sz, storage<Entity, std::remove_const_t<Owned>> *... owned, storage<Entity, std::remove_const_t<Get>> *... get) ENTT_NOEXCEPT
|
||||
: length{sz},
|
||||
@@ -495,11 +510,6 @@ public:
|
||||
* There are no guarantees on the order of the components. Use `begin` and
|
||||
* `end` if you want to iterate the group in the expected order.
|
||||
*
|
||||
* @warning
|
||||
* Empty components aren't explicitly instantiated. Only one instance of the
|
||||
* given type is created. Therefore, this function always returns a pointer
|
||||
* to that instance.
|
||||
*
|
||||
* @tparam Component Type of component in which one is interested.
|
||||
* @return A pointer to the array of components.
|
||||
*/
|
||||
@@ -630,14 +640,13 @@ public:
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<typename... Component>
|
||||
std::conditional_t<sizeof...(Component) == 1, std::tuple_element_t<0, std::tuple<Component &...>>, std::tuple<Component &...>>
|
||||
get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
||||
decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(contains(entt));
|
||||
|
||||
if constexpr(sizeof...(Component) == 1) {
|
||||
return (std::get<pool_type<Component> *>(pools)->get(entt), ...);
|
||||
} else {
|
||||
return std::tuple<Component &...>{get<Component>(entt)...};
|
||||
return std::tuple<decltype(get<Component>(entt))...>{get<Component>(entt)...};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,6 +665,11 @@ public:
|
||||
* void(Owned &..., Get &...);
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated. Therefore, temporary objects
|
||||
* are returned during iterations. They can be caught only by copy or with
|
||||
* const references.
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
@@ -665,7 +679,7 @@ public:
|
||||
[[maybe_unused]] auto data = std::get<0>(pools)->sparse_set<entity_type>::end() - *length;
|
||||
|
||||
for(auto next = *length; next; --next) {
|
||||
if constexpr(std::is_invocable_v<Func, std::add_lvalue_reference_t<Owned>..., std::add_lvalue_reference_t<Get>...>) {
|
||||
if constexpr(std::is_invocable_v<Func, decltype(get<Owned>({}))..., decltype(get<Get>({}))...>) {
|
||||
if constexpr(sizeof...(Get) == 0) {
|
||||
func(*(std::get<component_iterator_type<Owned>>(raw)++)...);
|
||||
} else {
|
||||
@@ -747,8 +761,7 @@ public:
|
||||
while(curr != next) {
|
||||
const auto lhs = copy[curr];
|
||||
const auto rhs = copy[next];
|
||||
(std::swap(std::get<pool_type<Owned> *>(pools)->raw()[lhs], std::get<pool_type<Owned> *>(pools)->raw()[rhs]), ...);
|
||||
(std::get<pool_type<Owned> *>(pools)->swap(lhs, rhs), ...);
|
||||
(swap<Owned>(0, std::get<pool_type<Owned> *>(pools), lhs, rhs), ...);
|
||||
copy[curr] = curr;
|
||||
curr = next;
|
||||
next = copy[curr];
|
||||
|
||||
@@ -564,8 +564,7 @@ public:
|
||||
* just created otherwise.
|
||||
*/
|
||||
template<typename... Component>
|
||||
std::conditional_t<sizeof...(Component) == 0, entity_type, std::tuple<entity_type, Component &...>>
|
||||
create() {
|
||||
decltype(auto) create() {
|
||||
entity_type entity;
|
||||
|
||||
if(available) {
|
||||
@@ -584,7 +583,7 @@ public:
|
||||
if constexpr(sizeof...(Component) == 0) {
|
||||
return entity;
|
||||
} else {
|
||||
return { entity, assign<Component>(entity)... };
|
||||
return std::tuple<entity_type, decltype(assign<Component>(entity))...>{entity, assign<Component>(entity)...};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,8 +601,7 @@ public:
|
||||
* sorted the same of the entities otherwise.
|
||||
*/
|
||||
template<typename... Component, typename It>
|
||||
std::conditional_t<sizeof...(Component) == 0, void, std::tuple<Component *...>>
|
||||
create(It first, It last) {
|
||||
auto 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(std::distance(first, last));
|
||||
const auto sz = std::min(available, length);
|
||||
@@ -630,7 +628,7 @@ public:
|
||||
});
|
||||
|
||||
if constexpr(sizeof...(Component) > 0) {
|
||||
return { assure<Component>()->batch(*this, first, last)... };
|
||||
return std::make_tuple(assure<Component>()->batch(*this, first, last)...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,17 +781,19 @@ public:
|
||||
if constexpr(sizeof...(Component) == 1) {
|
||||
return (pool<Component>()->get(entity), ...);
|
||||
} else {
|
||||
return std::tuple<std::add_const_t<Component> &...>{get<Component>(entity)...};
|
||||
return std::tuple<decltype(get<Component>(entity))...>{get<Component>(entity)...};
|
||||
}
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
template<typename... Component>
|
||||
inline decltype(auto) get([[maybe_unused]] const entity_type entity) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(valid(entity));
|
||||
|
||||
if constexpr(sizeof...(Component) == 1) {
|
||||
return (const_cast<Component &>(std::as_const(*this).template get<Component>(entity)), ...);
|
||||
return (pool<Component>()->get(entity), ...);
|
||||
} else {
|
||||
return std::tuple<Component &...>{get<Component>(entity)...};
|
||||
return std::tuple<decltype(get<Component>(entity))...>{get<Component>(entity)...};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -825,8 +825,7 @@ public:
|
||||
decltype(auto) get_or_assign(const entity_type entity, Args &&... args) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(valid(entity));
|
||||
auto *cpool = assure<Component>();
|
||||
auto *comp = cpool->try_get(entity);
|
||||
return comp ? *comp : cpool->assign(*this, entity, std::forward<Args>(args)...);
|
||||
return cpool->has(entity) ? cpool->get(entity) : cpool->assign(*this, entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -620,19 +620,6 @@ public:
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator_type = iterator;
|
||||
|
||||
/**
|
||||
* @brief Direct access to the array of objects.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated. Therefore, this function
|
||||
* always returns a null pointer.
|
||||
*
|
||||
* @return A pointer to the array of objects.
|
||||
*/
|
||||
const object_type * raw() const ENTT_NOEXCEPT {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
@@ -698,21 +685,6 @@ public:
|
||||
ENTT_ASSERT(underlying_type::has(entt));
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the object associated with an entity, if any.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated. Therefore, this function
|
||||
* always returns a null pointer.
|
||||
*
|
||||
* @param entt A valid entity identifier.
|
||||
* @return The object associated with the entity, if any.
|
||||
*/
|
||||
const object_type * try_get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(underlying_type::has(entt));
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/*! @copydoc basic_storage */
|
||||
|
||||
@@ -179,7 +179,7 @@ class basic_view {
|
||||
auto curr = raw++;
|
||||
|
||||
if((std::get<pool_type<Other> *>(pools)->has(entity) && ...)) {
|
||||
if constexpr(std::is_invocable_v<Func, std::add_lvalue_reference_t<Component>...>) {
|
||||
if constexpr(std::is_invocable_v<Func, decltype(get<Component>({}))...>) {
|
||||
func(get<Comp, Component>(curr, std::get<pool_type<Component> *>(pools), entity)...);
|
||||
} else {
|
||||
func(entity, get<Comp, Component>(curr, std::get<pool_type<Component> *>(pools), entity)...);
|
||||
@@ -244,11 +244,6 @@ public:
|
||||
* 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.
|
||||
*
|
||||
* @warning
|
||||
* Empty components aren't explicitly instantiated. Only one instance of the
|
||||
* given type is created. Therefore, this function always returns a pointer
|
||||
* to that instance.
|
||||
*
|
||||
* @tparam Comp Type of component in which one is interested.
|
||||
* @return A pointer to the array of components.
|
||||
*/
|
||||
@@ -354,14 +349,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<Comp &...>>, std::tuple<Comp &...>>
|
||||
get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
||||
decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(contains(entt));
|
||||
|
||||
if constexpr(sizeof...(Comp) == 1) {
|
||||
return (std::get<pool_type<Comp> *>(pools)->get(entt), ...);
|
||||
} else {
|
||||
return std::tuple<Comp &...>{get<Comp>(entt)...};
|
||||
return std::tuple<decltype(get<Comp>(entt))...>{get<Comp>(entt)...};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,6 +374,11 @@ public:
|
||||
* void(Component &...);
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated. Therefore, temporary objects
|
||||
* are returned during iterations. They can be caught only by copy or with
|
||||
* const references.
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
@@ -410,13 +409,19 @@ public:
|
||||
* It is no longer guaranteed that the performance is the best possible, but
|
||||
* there will be greater control over the order of iteration.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated. Therefore, temporary objects
|
||||
* are returned during iterations. They can be caught only by copy or with
|
||||
* const references.
|
||||
*
|
||||
* @tparam Comp Type of component to use to enforce the iteration order.
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Comp, typename Func>
|
||||
inline void each(Func func) const {
|
||||
each<Comp>(type_list_cat_t<std::conditional_t<std::is_same_v<Comp, Component>, type_list<>, type_list<Component>>...>{}, std::move(func));
|
||||
using other_type = type_list_cat_t<std::conditional_t<std::is_same_v<Comp, Component>, type_list<>, type_list<Component>>...>;
|
||||
each<Comp>(other_type{}, std::move(func));
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -470,7 +475,7 @@ class basic_view<Entity, Component> {
|
||||
|
||||
public:
|
||||
/*! @brief Type of component iterated by the view. */
|
||||
using raw_type = std::remove_reference_t<decltype(std::declval<pool_type>().get(0))>;
|
||||
using raw_type = Component;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename pool_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
@@ -504,11 +509,6 @@ public:
|
||||
* 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.
|
||||
*
|
||||
* @warning
|
||||
* Empty components aren't explicitly instantiated. Only one instance of the
|
||||
* given type is created. Therefore, this function always returns a pointer
|
||||
* to that instance.
|
||||
*
|
||||
* @return A pointer to the array of components.
|
||||
*/
|
||||
raw_type * raw() const ENTT_NOEXCEPT {
|
||||
@@ -612,7 +612,7 @@ public:
|
||||
* @param entt A valid entity identifier.
|
||||
* @return The component assigned to the entity.
|
||||
*/
|
||||
raw_type & get(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
decltype(auto) get(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(contains(entt));
|
||||
return pool->get(entt);
|
||||
}
|
||||
@@ -632,12 +632,17 @@ public:
|
||||
* void(Component &);
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated. Therefore, temporary objects
|
||||
* are returned during iterations. They can be caught only by copy or with
|
||||
* const references.
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
if constexpr(std::is_invocable_v<Func, std::add_lvalue_reference_t<Component>>) {
|
||||
if constexpr(std::is_invocable_v<Func, decltype(get({}))>) {
|
||||
std::for_each(pool->begin(), pool->end(), std::move(func));
|
||||
} else {
|
||||
std::for_each(pool->sparse_set<Entity>::begin(), pool->sparse_set<Entity>::end(), [&func, raw = pool->begin()](const auto entt) mutable {
|
||||
|
||||
@@ -242,10 +242,8 @@ TEST(NonOwningGroup, ConstNonConstAndAllInBetween) {
|
||||
ASSERT_EQ(group.size(), decltype(group.size()){1});
|
||||
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.get<int>(0)), int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.get<const int>(0)), const int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.get<const char>(0)), const char &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.get<int, const char>(0)), std::tuple<int &, const char &>>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.get<const int, const char>(0)), std::tuple<const int &, const char &>>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.raw<const char>()), const char *>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.raw<int>()), int *>));
|
||||
|
||||
@@ -740,13 +738,10 @@ TEST(OwningGroup, ConstNonConstAndAllInBetween) {
|
||||
ASSERT_EQ(group.size(), decltype(group.size()){1});
|
||||
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.get<int>(0)), int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.get<const int>(0)), const int &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.get<const char>(0)), const char &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.get<double>(0)), double &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.get<const double>(0)), const double &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.get<const float>(0)), const float &>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.get<int, const char, double, const float>(0)), std::tuple<int &, const char &, double &, const float &>>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.get<const int, const char, const double, const float>(0)), std::tuple<const int &, const char &, const double &, const float &>>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.raw<const float>()), const float *>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.raw<double>()), double *>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(group.raw<const char>()), const char *>));
|
||||
|
||||
@@ -1002,7 +1002,7 @@ TEST(Registry, CreateManyEntitiesAtOnce) {
|
||||
|
||||
TEST(Registry, CreateAnEntityWithComponents) {
|
||||
entt::registry registry;
|
||||
const auto &[entity, ivalue, cvalue] = registry.create<int, char>();
|
||||
auto &&[entity, ivalue, cvalue] = registry.create<int, char>();
|
||||
|
||||
ASSERT_FALSE(registry.empty<int>());
|
||||
ASSERT_FALSE(registry.empty<char>());
|
||||
|
||||
@@ -87,8 +87,6 @@ TEST(Storage, EmptyType) {
|
||||
|
||||
ASSERT_TRUE(set.has(42));
|
||||
ASSERT_TRUE(set.has(99));
|
||||
ASSERT_EQ(set.try_get(42), nullptr);
|
||||
ASSERT_EQ(std::as_const(set).try_get(42), std::as_const(set).try_get(99));
|
||||
|
||||
auto &&component = set.get(42);
|
||||
|
||||
@@ -314,15 +312,6 @@ TEST(Storage, Raw) {
|
||||
ASSERT_EQ(*(set.raw() + 2u), 9);
|
||||
}
|
||||
|
||||
TEST(Storage, RawEmptyType) {
|
||||
entt::storage<std::uint64_t, empty_type> set;
|
||||
|
||||
set.construct(3);
|
||||
|
||||
ASSERT_EQ(set.raw(), std::as_const(set).raw());
|
||||
ASSERT_EQ(set.try_get(3), set.raw());
|
||||
}
|
||||
|
||||
TEST(Storage, SortOrdered) {
|
||||
entt::storage<std::uint64_t, boxed_int> set;
|
||||
|
||||
|
||||
@@ -398,10 +398,8 @@ TEST(MultipleComponentView, ConstNonConstAndAllInBetween) {
|
||||
ASSERT_EQ(view.size(), decltype(view.size()){1});
|
||||
|
||||
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 &>>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.raw<const char>()), const char *>));
|
||||
ASSERT_TRUE((std::is_same_v<decltype(view.raw<int>()), int *>));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user