runtime view: allocator support

This commit is contained in:
Michele Caini
2022-06-29 07:56:45 +02:00
parent e65b3a790d
commit bcfd6d1b4f
4 changed files with 227 additions and 51 deletions

1
TODO
View File

@@ -13,7 +13,6 @@ DOC:
* update entity doc when the storage based model is in place
WIP:
* runtime view: allocator support, storage access
* get rid of observers, storage based views made them pointless - document alternatives
* dense_map/set and registry: move ctor/op should be noexcept (op is conditionally noexcept though)
* add storage getter for filters to views and groups

View File

@@ -31,7 +31,7 @@ class basic_registry;
template<typename, typename, typename = void>
class basic_view;
template<typename Type>
template<typename Type, typename = std::allocator<Type *>>
class basic_runtime_view;
template<typename, typename, typename>

View File

@@ -138,9 +138,17 @@ private:
* In any other case, attempting to use a view results in undefined behavior.
*
* @tparam Type Common base type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type>
struct basic_runtime_view {
template<typename Type, typename Allocator>
class basic_runtime_view {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type *>, "Invalid value type");
using container_type = std::vector<Type *, Allocator>;
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Underlying entity identifier. */
using entity_type = typename Type::entity_type;
/*! @brief Unsigned integer type. */
@@ -152,8 +160,75 @@ struct basic_runtime_view {
/*! @brief Default constructor to use to create empty, invalid views. */
basic_runtime_view() noexcept
: pools{},
filter{} {}
: basic_runtime_view{allocator_type{}} {}
/**
* @brief Constructs an empty, invalid view with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_runtime_view(const allocator_type &allocator)
: pools{allocator},
filter{allocator} {}
/*! @brief Default copy constructor. */
basic_runtime_view(const basic_runtime_view &other) = default;
/**
* @brief Allocator-extended copy constructor.
* @param other The instance to copy from.
* @param allocator The allocator to use.
*/
basic_runtime_view(const basic_runtime_view &other, const allocator_type &allocator)
: pools{other.pools, allocator},
filter{other.filter, allocator} {}
/*! @brief Default move constructor. */
basic_runtime_view(basic_runtime_view &&other) noexcept(std::is_nothrow_move_constructible_v<container_type>) = default;
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_runtime_view(basic_runtime_view &&other, const allocator_type &allocator)
: pools{std::move(other.pools), allocator},
filter{std::move(other.filter), allocator} {}
/**
* @brief Default copy assignment operator.
* @return This container.
*/
basic_runtime_view &operator=(const basic_runtime_view &other) = default;
/**
* @brief Default move assignment operator.
* @return This container.
*/
basic_runtime_view &operator=(basic_runtime_view &&other) noexcept(std::is_nothrow_move_assignable_v<container_type>) = default;
/**
* @brief Exchanges the contents with those of a given view.
* @param other View to exchange the content with.
*/
void swap(basic_runtime_view &other) {
using std::swap;
swap(pools, other.pools);
swap(filter, other.filter);
}
/**
* @brief Returns the associated allocator.
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
return pools.get_allocator();
}
/*! @brief Clears the view. */
void clear() {
pools.clear();
filter.clear();
}
/**
* @brief Appends an opaque storage object to a runtime view.
@@ -251,8 +326,8 @@ struct basic_runtime_view {
}
private:
std::vector<base_type *> pools;
std::vector<base_type *> filter;
container_type pools;
container_type filter;
};
} // namespace entt

View File

@@ -71,13 +71,35 @@ TYPED_TEST(RuntimeView, Functionalities) {
ASSERT_EQ(registry.get<char>(entity), '2');
}
entt::runtime_view empty{};
view.clear();
ASSERT_EQ(empty.size_hint(), 0u);
ASSERT_EQ(empty.begin(), empty.end());
ASSERT_EQ(view.size_hint(), 0u);
ASSERT_EQ(view.begin(), view.end());
}
TYPED_TEST(RuntimeView, Iterator) {
TYPED_TEST(RuntimeView, Constructors) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
registry.emplace<int>(entity);
view = runtime_view_type{std::allocator<int>{}};
view.iterate(registry.storage<int>());
ASSERT_TRUE(view.contains(entity));
runtime_view_type temp{view, view.get_allocator()};
runtime_view_type other{std::move(temp), view.get_allocator()};
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(temp.contains(entity));
ASSERT_TRUE(other.contains(entity));
}
TYPED_TEST(RuntimeView, Copy) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
@@ -87,8 +109,97 @@ TYPED_TEST(RuntimeView, Iterator) {
registry.emplace<int>(entity);
registry.emplace<char>(entity);
view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
using iterator = typename decltype(view)::iterator;
view.iterate(registry.storage<int>());
ASSERT_TRUE(view.contains(entity));
runtime_view_type other{view};
ASSERT_TRUE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
other.iterate(registry.storage<int>()).exclude(registry.storage<char>());
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(other.contains(entity));
other = view;
ASSERT_TRUE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
}
TYPED_TEST(RuntimeView, Move) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
view.iterate(registry.storage<int>());
ASSERT_TRUE(view.contains(entity));
runtime_view_type other{std::move(view)};
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
view = other;
other.iterate(registry.storage<int>()).exclude(registry.storage<char>());
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(other.contains(entity));
other = std::move(view);
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
}
TYPED_TEST(RuntimeView, Swap) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
runtime_view_type other{};
const auto entity = registry.create();
registry.emplace<int>(entity);
view.iterate(registry.storage<int>());
ASSERT_EQ(view.size_hint(), 1u);
ASSERT_EQ(other.size_hint(), 0u);
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(other.contains(entity));
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(other.begin(), other.end());
view.swap(other);
ASSERT_EQ(view.size_hint(), 0u);
ASSERT_EQ(other.size_hint(), 1u);
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
ASSERT_EQ(view.begin(), view.end());
ASSERT_NE(other.begin(), other.end());
}
TYPED_TEST(RuntimeView, Iterator) {
using runtime_view_type = typename TestFixture::type;
using iterator = typename runtime_view_type::iterator;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
registry.emplace<int>(entity);
view.iterate(registry.storage<int>());
iterator end{view.begin()};
iterator begin{};
@@ -115,20 +226,18 @@ TYPED_TEST(RuntimeView, Contains) {
entt::registry registry;
runtime_view_type view{};
const auto e0 = registry.create();
registry.emplace<int>(e0);
registry.emplace<char>(e0);
const auto entity = registry.create();
const auto other = registry.create();
const auto e1 = registry.create();
registry.emplace<int>(e1);
registry.emplace<char>(e1);
registry.emplace<int>(entity);
registry.emplace<int>(other);
registry.destroy(e0);
registry.destroy(entity);
view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
view.iterate(registry.storage<int>());
ASSERT_FALSE(view.contains(e0));
ASSERT_TRUE(view.contains(e1));
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(view.contains(other));
}
TYPED_TEST(RuntimeView, Empty) {
@@ -137,24 +246,19 @@ TYPED_TEST(RuntimeView, Empty) {
entt::registry registry;
runtime_view_type view{};
const auto e0 = registry.create();
registry.emplace<double>(e0);
registry.emplace<int>(e0);
registry.emplace<float>(e0);
const auto entity = registry.create();
const auto other = registry.create();
const auto e1 = registry.create();
registry.emplace<char>(e1);
registry.emplace<float>(e1);
registry.emplace<double>(entity);
registry.emplace<float>(other);
view.iterate(registry.storage<int>())
.iterate(registry.storage<char>())
.iterate(registry.storage<float>());
view.iterate(registry.storage<int>());
ASSERT_FALSE(view.contains(e0));
ASSERT_FALSE(view.contains(e1));
ASSERT_FALSE(view.contains(entity));
ASSERT_FALSE(view.contains(other));
ASSERT_EQ(view.begin(), view.end());
ASSERT_EQ((std::find(view.begin(), view.end(), e0)), view.end());
ASSERT_EQ((std::find(view.begin(), view.end(), e1)), view.end());
ASSERT_EQ((std::find(view.begin(), view.end(), entity)), view.end());
ASSERT_EQ((std::find(view.begin(), view.end(), other)), view.end());
}
TYPED_TEST(RuntimeView, Each) {
@@ -163,17 +267,16 @@ TYPED_TEST(RuntimeView, Each) {
entt::registry registry;
runtime_view_type view{};
const auto e0 = registry.create();
registry.emplace<int>(e0);
registry.emplace<char>(e0);
const auto entity = registry.create();
const auto other = registry.create();
const auto e1 = registry.create();
registry.emplace<char>(e1);
registry.emplace<int>(entity);
registry.emplace<char>(other);
view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
view.each([e0](const auto entt) {
ASSERT_EQ(entt, e0);
view.each([entity](const auto entt) {
ASSERT_EQ(entt, entity);
});
}
@@ -195,8 +298,8 @@ TYPED_TEST(RuntimeView, EachWithHoles) {
view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
view.each([e0](auto entity) {
ASSERT_EQ(e0, entity);
view.each([e0](auto entt) {
ASSERT_EQ(e0, entt);
});
}
@@ -214,14 +317,13 @@ TYPED_TEST(RuntimeView, ExcludedComponents) {
registry.emplace<char>(e1);
view.iterate(registry.storage<int>())
.exclude(registry.storage<char>())
.exclude(registry.storage<double>());
.exclude(registry.storage<char>());
ASSERT_TRUE(view.contains(e0));
ASSERT_FALSE(view.contains(e1));
view.each([e0](auto entity) {
ASSERT_EQ(e0, entity);
view.each([e0](auto entt) {
ASSERT_EQ(e0, entt);
});
}