Skip to content

Commit

Permalink
helper: storage-based to_entity function
Browse files Browse the repository at this point in the history
  • Loading branch information
skypjack committed Oct 17, 2023
1 parent 173c4e1 commit 9d33ae8
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 58 deletions.
2 changes: 1 addition & 1 deletion TODO
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ TODO (high prio):
* update view doc: single vs multi type views are no longer a thing actually
* further improve meta resolve function by id (bimap)
* make ::pack part of the sparse set interface
* storage-based to_entity and invoke helpers
* storage-based invoke helpers (update sigh mixin to also support storage based cbs)
* make nth_argument use function types rather than pointers
* ===> TEST: view (scoped begin) tests after the last changes

Expand Down
41 changes: 28 additions & 13 deletions src/entt/entity/helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "../signal/delegate.hpp"
#include "fwd.hpp"
#include "group.hpp"
#include "storage.hpp"
#include "view.hpp"

namespace entt {
Expand Down Expand Up @@ -115,27 +116,41 @@ void invoke(Registry &reg, const typename Registry::entity_type entt) {
* @brief Returns the entity associated with a given component.
*
* @warning
* Currently, this function only works correctly with the default pool as it
* Currently, this function only works correctly with the default storage as it
* makes assumptions about how the components are laid out.
*
* @tparam Registry Basic registry type.
* @tparam Args Storage type template parameters.
* @param storage A storage that contains the given component.
* @param instance A valid component instance.
* @return The entity associated with the given component.
*/
template<typename... Args>
auto to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) -> typename basic_storage<Args...>::entity_type {
constexpr auto page_size = basic_storage<Args...>::traits_type::page_size;
const typename basic_storage<Args...>::base_type &base = storage;
const auto *addr = std::addressof(instance);

for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) {
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(page_size)) {
return *(it + dist);
}
}

return null;
}

/**
* @copybrief to_entity
* @tparam Args Registry type template parameters.
* @tparam Component Type of component.
* @param reg A registry that contains the given entity and its components.
* @param instance A valid component instance.
* @return The entity associated with the given component.
*/
template<typename Registry, typename Component>
typename Registry::entity_type to_entity(const Registry &reg, const Component &instance) {
template<typename... Args, typename Component>
[[deprecated("use storage based to_entity instead")]] typename basic_registry<Args...>::entity_type to_entity(const basic_registry<Args...> &reg, const Component &instance) {
if(const auto *storage = reg.template storage<Component>(); storage) {
constexpr auto page_size = std::remove_const_t<std::remove_pointer_t<decltype(storage)>>::traits_type::page_size;
const typename Registry::common_type &base = *storage;
const auto *addr = std::addressof(instance);

for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) {
if(const auto dist = (addr - std::addressof(storage->get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(page_size)) {
return *(it + dist);
}
}
return to_entity(*storage, instance);
}

return null;
Expand Down
108 changes: 64 additions & 44 deletions test/entt/entity/helper.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <gtest/gtest.h>
#include <entt/entity/component.hpp>
#include <entt/entity/entity.hpp>
#include <entt/entity/helper.hpp>
#include <entt/entity/registry.hpp>
Expand All @@ -11,16 +12,29 @@ struct clazz {
entt::entity entt{entt::null};
};

struct stable_type {
static constexpr auto in_place_delete = true;
int value;
};

void sigh_callback(int &value) {
++value;
}

TEST(Helper, AsView) {
struct pointer_stable {
static constexpr auto in_place_delete = true;
int value{};
};

template<typename Type>
struct ToEntity: testing::Test {
using type = Type;
};

template<typename Type>
using ToEntityDeprecated = ToEntity<Type>;

using ToEntityTypes = ::testing::Types<int, pointer_stable>;

TYPED_TEST_SUITE(ToEntity, ToEntityTypes, );
TYPED_TEST_SUITE(ToEntityDeprecated, ToEntityTypes, );

TEST(AsView, Functionalities) {
entt::registry registry;
const entt::registry cregistry;

Expand All @@ -30,7 +44,7 @@ TEST(Helper, AsView) {
([](entt::view<entt::get_t<const char, const double>, entt::exclude_t<const int>>) {})(entt::as_view{cregistry});
}

TEST(Helper, AsGroup) {
TEST(AsGroup, Functionalities) {
entt::registry registry;
const entt::registry cregistry;

Expand All @@ -39,7 +53,7 @@ TEST(Helper, AsGroup) {
([](entt::group<entt::owned_t<const double>, entt::get_t<const char>, entt::exclude_t<const int>>) {})(entt::as_group{cregistry});
}

TEST(Helper, Invoke) {
TEST(Invoke, Functionalities) {
entt::registry registry;
const auto entity = registry.create();

Expand All @@ -49,87 +63,93 @@ TEST(Helper, Invoke) {
ASSERT_EQ(entity, registry.get<clazz>(entity).entt);
}

TEST(Helper, ToEntity) {
TYPED_TEST(ToEntity, Functionalities) {
using value_type = typename TestFixture::type;
using traits_type = entt::component_traits<value_type>;

entt::registry registry;
const entt::entity null = entt::null;
constexpr auto page_size = entt::storage_type_t<int>::traits_type::page_size;
const int value = 42;
auto &storage = registry.storage<value_type>();
constexpr auto page_size = entt::storage_type_t<value_type>::traits_type::page_size;
const value_type value{42};

ASSERT_EQ(entt::to_entity(registry, 42), null);
ASSERT_EQ(entt::to_entity(registry, value), null);
ASSERT_EQ(entt::to_entity(storage, value_type{42}), null);
ASSERT_EQ(entt::to_entity(storage, value), null);

const auto entity = registry.create();
auto &&storage = registry.storage<int>();
storage.emplace(entity);

while(storage.size() < (page_size - 1u)) {
while(storage.size() < (page_size - (1u + traits_type::in_place_delete))) {
storage.emplace(registry.create(), value);
}

const auto other = registry.create();
const auto next = registry.create();

registry.emplace<int>(other);
registry.emplace<int>(next);
registry.emplace<value_type>(other);
registry.emplace<value_type>(next);

ASSERT_EQ(entt::to_entity(registry, registry.get<int>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<int>(other)), other);
ASSERT_EQ(entt::to_entity(registry, registry.get<int>(next)), next);
ASSERT_EQ(entt::to_entity(storage, registry.get<value_type>(entity)), entity);
ASSERT_EQ(entt::to_entity(storage, registry.get<value_type>(other)), other);
ASSERT_EQ(entt::to_entity(storage, registry.get<value_type>(next)), next);

ASSERT_EQ(&registry.get<int>(entity) + page_size - 1u, &registry.get<int>(other));
ASSERT_EQ(&registry.get<value_type>(entity) + page_size - (1u + traits_type::in_place_delete), &registry.get<value_type>(other));

registry.destroy(other);

ASSERT_EQ(entt::to_entity(registry, registry.get<int>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<int>(next)), next);
ASSERT_EQ(entt::to_entity(storage, registry.get<value_type>(entity)), entity);
ASSERT_EQ(entt::to_entity(storage, registry.get<value_type>(next)), next);

ASSERT_EQ(&registry.get<int>(entity) + page_size - 1u, &registry.get<int>(next));
ASSERT_EQ(&registry.get<value_type>(entity) + page_size - 1u, &registry.get<value_type>(next));

ASSERT_EQ(entt::to_entity(registry, 42), null);
ASSERT_EQ(entt::to_entity(registry, value), null);
ASSERT_EQ(entt::to_entity(storage, value_type{42}), null);
ASSERT_EQ(entt::to_entity(storage, value), null);
}

TEST(Helper, ToEntityStableType) {
TYPED_TEST(ToEntityDeprecated, Functionalities) {
using value_type = typename TestFixture::type;
using traits_type = entt::component_traits<value_type>;

entt::registry registry;
const entt::entity null = entt::null;
constexpr auto page_size = entt::storage_type_t<stable_type>::traits_type::page_size;
const stable_type value{42};
constexpr auto page_size = entt::storage_type_t<value_type>::traits_type::page_size;
const value_type value{42};

ASSERT_EQ(entt::to_entity(registry, stable_type{42}), null);
ASSERT_EQ(entt::to_entity(registry, value_type{42}), null);
ASSERT_EQ(entt::to_entity(registry, value), null);

const auto entity = registry.create();
auto &&storage = registry.storage<stable_type>();
registry.emplace<stable_type>(entity);
auto &&storage = registry.storage<value_type>();
storage.emplace(entity);

while(storage.size() < (page_size - 2u)) {
while(storage.size() < (page_size - (1u + traits_type::in_place_delete))) {
storage.emplace(registry.create(), value);
}

const auto other = registry.create();
const auto next = registry.create();

registry.emplace<stable_type>(other);
registry.emplace<stable_type>(next);
registry.emplace<value_type>(other);
registry.emplace<value_type>(next);

ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(other)), other);
ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(next)), next);
ASSERT_EQ(entt::to_entity(registry, registry.get<value_type>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<value_type>(other)), other);
ASSERT_EQ(entt::to_entity(registry, registry.get<value_type>(next)), next);

ASSERT_EQ(&registry.get<stable_type>(entity) + page_size - 2u, &registry.get<stable_type>(other));
ASSERT_EQ(&registry.get<value_type>(entity) + page_size - (1u + traits_type::in_place_delete), &registry.get<value_type>(other));

registry.destroy(other);

ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(next)), next);
ASSERT_EQ(entt::to_entity(registry, registry.get<value_type>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<value_type>(next)), next);

ASSERT_EQ(&registry.get<stable_type>(entity) + page_size - 1u, &registry.get<stable_type>(next));
ASSERT_EQ(&registry.get<value_type>(entity) + page_size - 1u, &registry.get<value_type>(next));

ASSERT_EQ(entt::to_entity(registry, stable_type{42}), null);
ASSERT_EQ(entt::to_entity(registry, value_type{42}), null);
ASSERT_EQ(entt::to_entity(registry, value), null);
}

TEST(Helper, SighHelper) {
TEST(SighHelper, Functionalities) {
using namespace entt::literals;

entt::registry registry{};
Expand Down

0 comments on commit 9d33ae8

Please sign in to comment.