view: allow swapping storage elements of a view

This commit is contained in:
Michele Caini
2023-04-19 09:29:31 +02:00
parent c74900057c
commit 80563b9557
3 changed files with 122 additions and 4 deletions

1
TODO
View File

@@ -7,6 +7,7 @@ DOC:
* examples (and credits) from @alanjfs :)
* update entity doc when the storage based model is in place
* in-place O(1) release/destroy for non-orphaned entities, out-of-sync model
* storage swap on views and empty views (once the static storage in the registry is removed)
TODO (high prio):
* check natvis files (periodically :)

View File

@@ -199,6 +199,7 @@ class basic_view;
*/
template<typename... Get, typename... Exclude>
class basic_view<get_t<Get...>, exclude_t<Exclude...>> {
static constexpr auto offset = sizeof...(Get);
using base_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
using underlying_type = typename base_type::entity_type;
@@ -267,7 +268,7 @@ public:
basic_view(Get &...value, Exclude &...excl) noexcept
: pools{&value...},
filter{&excl...},
view{std::get<0>(pools)} {
view{} {
refresh();
}
@@ -299,7 +300,8 @@ public:
/*! @brief Updates the internal leading view if required. */
void refresh() noexcept {
std::apply([this](auto *...elem) { ((this->view = elem->size() < this->view->size() ? elem : this->view), ...); }, pools);
view = std::get<0>(pools);
std::apply([this](auto *, auto *...other) { ((this->view = other->size() < this->view->size() ? other : this->view), ...); }, pools);
}
/**
@@ -327,8 +329,6 @@ public:
*/
template<std::size_t Index>
[[nodiscard]] auto *storage() const noexcept {
constexpr auto offset = sizeof...(Get);
if constexpr(Index < offset) {
return std::get<Index>(pools);
} else {
@@ -336,6 +336,36 @@ public:
}
}
/**
* @brief Assigns a storage to a view.
* @tparam Type Type of storage to assign to the view.
* @param elem A storage to assign to the view.
*/
template<typename Type>
void storage(Type &elem) noexcept {
storage<index_of<typename Type::value_type>>(elem);
}
/**
* @brief Assigns a storage to a view.
* @tparam Index Index of the storage to assign to the view.
* @tparam Type Type of storage to assign to the view.
* @param elem A storage to assign to the view.
*/
template<std::size_t Index, typename Type>
void storage(Type &elem) noexcept {
if constexpr(Index < offset) {
view = (std::get<Index>(pools) == view ? nullptr : view);
std::get<Index>(pools) = &elem;
} else {
std::get<Index - offset>(filter) = &elem;
}
if(view == nullptr && std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, pools)) {
refresh();
}
}
/**
* @brief Estimates the number of entities iterated by the view.
* @return Estimated number of entities iterated by the view.
@@ -610,6 +640,24 @@ public:
return std::get<Index>(pools);
}
/**
* @brief Assigns a storage to a view.
* @param elem A storage to assign to the view.
*/
void storage(Get &elem) noexcept {
storage<0>(elem);
}
/**
* @brief Assigns a storage to a view.
* @tparam Index Index of the storage to assign to the view.
* @param elem A storage to assign to the view.
*/
template<std::size_t Index>
void storage(Get &elem) noexcept {
view = std::get<Index>(pools) = &elem;
}
/**
* @brief Returns the number of entities that have the given component.
* @return Number of entities that have the given component.

View File

@@ -509,6 +509,41 @@ TEST(SingleComponentView, Storage) {
ASSERT_EQ(cview.storage<const char>(), nullptr);
}
TEST(SingleComponentView, SwapStorage) {
using namespace entt::literals;
entt::registry registry;
entt::basic_view<entt::get_t<entt::storage<int>>, entt::exclude_t<>> view;
entt::basic_view<entt::get_t<const entt::storage<int>>, entt::exclude_t<>> cview;
ASSERT_FALSE(view);
ASSERT_FALSE(cview);
ASSERT_EQ(view.storage<0u>(), nullptr);
ASSERT_EQ(cview.storage<const int>(), nullptr);
const entt::entity entity{42u};
registry.emplace<int>(entity);
view.storage(registry.storage<int>());
cview.storage(registry.storage<int>());
ASSERT_TRUE(view);
ASSERT_TRUE(cview);
ASSERT_NE(view.storage<0u>(), nullptr);
ASSERT_NE(cview.storage<const int>(), nullptr);
ASSERT_EQ(view.size(), 1u);
ASSERT_EQ(cview.size(), 1u);
ASSERT_TRUE(view.contains(entity));
ASSERT_TRUE(cview.contains(entity));
view.storage(registry.storage<int>("other"_hs));
cview.storage(registry.storage<int>("other"_hs));
ASSERT_TRUE(view.empty());
ASSERT_TRUE(cview.empty());
}
TEST(MultiComponentView, Functionalities) {
entt::registry registry;
auto view = registry.view<int, char>();
@@ -1338,6 +1373,40 @@ TEST(MultiComponentView, Storage) {
ASSERT_EQ(view.storage<const float>(), nullptr);
}
TEST(MultiComponentView, SwapStorage) {
using namespace entt::literals;
entt::registry registry;
entt::basic_view<entt::get_t<entt::storage<int>>, entt::exclude_t<const entt::storage<char>>> view;
ASSERT_FALSE(view);
ASSERT_EQ(view.storage<0u>(), nullptr);
ASSERT_EQ(view.storage<const char>(), nullptr);
const entt::entity entity{42u};
registry.emplace<int>(entity);
registry.emplace<char>(entity);
view.storage(registry.storage<int>());
view.storage<1u>(registry.storage<char>());
ASSERT_TRUE(view);
ASSERT_NE(view.storage<int>(), nullptr);
ASSERT_NE(view.storage<1u>(), nullptr);
ASSERT_EQ(view.size_hint(), 1u);
ASSERT_FALSE(view.contains(entity));
view.storage(registry.storage<char>("other"_hs));
ASSERT_EQ(view.size_hint(), 1u);
ASSERT_TRUE(view.contains(entity));
view.storage(registry.storage<int>("empty"_hs));
ASSERT_EQ(view.size_hint(), 0u);
}
TEST(View, Pipe) {
entt::registry registry;
const auto entity = registry.create();