Support const-qualified components in ecs::container::apply and add const-related docs
This commit is contained in:
parent
2a97a467aa
commit
eb87f1ea20
2 changed files with 56 additions and 15 deletions
|
|
@ -122,6 +122,8 @@ namespace psemek::ecs
|
||||||
void detach(handle const & entity);
|
void detach(handle const & entity);
|
||||||
|
|
||||||
/** Create a query cache that can be used to speed up `apply()` calls.
|
/** Create a query cache that can be used to speed up `apply()` calls.
|
||||||
|
*
|
||||||
|
* The constness of the component types is ignored.
|
||||||
*
|
*
|
||||||
* @tparam Components The component types matching the corresponding `apply()` call
|
* @tparam Components The component types matching the corresponding `apply()` call
|
||||||
* @return A query cache
|
* @return A query cache
|
||||||
|
|
@ -137,6 +139,11 @@ namespace psemek::ecs
|
||||||
* void(container, components...)
|
* void(container, components...)
|
||||||
* void(container, handle, components...)
|
* void(container, handle, components...)
|
||||||
*
|
*
|
||||||
|
* The component types can be const-qualified, in which case the corresponding function must
|
||||||
|
* also accept the corresponding components by a const reference. This can be used to indicate
|
||||||
|
* that this function doesn't modify specific components, to prevent modification callbacks
|
||||||
|
* from being triggered.
|
||||||
|
*
|
||||||
* The function can freely create or destroy entities, and or attach/detach
|
* The function can freely create or destroy entities, and or attach/detach
|
||||||
* components to existing entities. It is unspecified whether the function
|
* components to existing entities. It is unspecified whether the function
|
||||||
* will or will not visit newly created entities during this `apply()` call.
|
* will or will not visit newly created entities during this `apply()` call.
|
||||||
|
|
@ -150,7 +157,8 @@ namespace psemek::ecs
|
||||||
* @param function The function to apply to all entities with the specified components
|
* @param function The function to apply to all entities with the specified components
|
||||||
* @param cache A query cache
|
* @param cache A query cache
|
||||||
* @return The query cache, either user-provided, or newly-created
|
* @return The query cache, either user-provided, or newly-created
|
||||||
* @pre The query query cache was created with the exact same sequence of component types
|
* @pre The query query cache was created with the exact same sequence of component types, ignoring
|
||||||
|
* constness
|
||||||
* @warning If any two of the passed component types are equal, the call fails with
|
* @warning If any two of the passed component types are equal, the call fails with
|
||||||
* a compilation error
|
* a compilation error
|
||||||
* @warning If the function accesses passed components after destroying the
|
* @warning If the function accesses passed components after destroying the
|
||||||
|
|
@ -172,6 +180,11 @@ namespace psemek::ecs
|
||||||
* void(container, span<components>...)
|
* void(container, span<components>...)
|
||||||
* void(container, span<handle const>, span<components>...)
|
* void(container, span<handle const>, span<components>...)
|
||||||
*
|
*
|
||||||
|
* The component types can be const-qualified, in which case the corresponding function must
|
||||||
|
* also accept the corresponding components by views to const. This can be used to indicate
|
||||||
|
* that this function doesn't modify specific components, to prevent modification callbacks
|
||||||
|
* from being triggered.
|
||||||
|
*
|
||||||
* The sizes of all spans within a single function call are the same,
|
* The sizes of all spans within a single function call are the same,
|
||||||
* except for empty component types (i.e. std::is_empty_v<Component> is true),
|
* except for empty component types (i.e. std::is_empty_v<Component> is true),
|
||||||
* each having an unspecified non-zero size. If all components are empty types,
|
* each having an unspecified non-zero size. If all components are empty types,
|
||||||
|
|
@ -185,7 +198,8 @@ namespace psemek::ecs
|
||||||
* @param function The function to batch-apply to all entities with the specified components
|
* @param function The function to batch-apply to all entities with the specified components
|
||||||
* @param cache A query cache
|
* @param cache A query cache
|
||||||
* @return The query cache, either user-provided, or newly-created
|
* @return The query cache, either user-provided, or newly-created
|
||||||
* @pre The query query cache was created with the exact same sequence of component types
|
* @pre The query query cache was created with the exact same sequence of component types, ignoring
|
||||||
|
* constness
|
||||||
* @warning If any two of the passed component types are equal, the call fails with
|
* @warning If any two of the passed component types are equal, the call fails with
|
||||||
* a compilation error
|
* a compilation error
|
||||||
* @warning If the function creates or destroyes entities, or attaches or detaches components,
|
* @warning If the function creates or destroyes entities, or attaches or detaches components,
|
||||||
|
|
@ -201,6 +215,9 @@ namespace psemek::ecs
|
||||||
*
|
*
|
||||||
* The entity is considered `alive()` during the constructor invocation.
|
* The entity is considered `alive()` during the constructor invocation.
|
||||||
*
|
*
|
||||||
|
* The component types can be const-qualified, in which case the corresponding function must
|
||||||
|
* also accept the corresponding components by a const reference.
|
||||||
|
*
|
||||||
* When attaching components to an entity, the constructor is called
|
* When attaching components to an entity, the constructor is called
|
||||||
* exactly when the entity didn't match the constructor's component types
|
* exactly when the entity didn't match the constructor's component types
|
||||||
* before attaching new components, and does match them after attaching.
|
* before attaching new components, and does match them after attaching.
|
||||||
|
|
@ -210,6 +227,9 @@ namespace psemek::ecs
|
||||||
*
|
*
|
||||||
* The constructor can immediately destroy the created entity.
|
* The constructor can immediately destroy the created entity.
|
||||||
*
|
*
|
||||||
|
* The constuctor is not considered to be a modification of the entity, i.e. it
|
||||||
|
* doesn't trigger modification callbacks.
|
||||||
|
*
|
||||||
* @param function A function to be applied to created entity's components
|
* @param function A function to be applied to created entity's components
|
||||||
* @return An ownerwhip token; destroying this token removes
|
* @return An ownerwhip token; destroying this token removes
|
||||||
* the constructor from this container
|
* the constructor from this container
|
||||||
|
|
@ -230,6 +250,9 @@ namespace psemek::ecs
|
||||||
*
|
*
|
||||||
* The entity is considered `alive()` during the destructor invocation.
|
* The entity is considered `alive()` during the destructor invocation.
|
||||||
*
|
*
|
||||||
|
* The component types can be const-qualified, in which case the corresponding function must
|
||||||
|
* also accept the corresponding components by a const reference.
|
||||||
|
*
|
||||||
* When detaching components from an entity, the destructor is called
|
* When detaching components from an entity, the destructor is called
|
||||||
* exactly when the entity did match the destructor's component types
|
* exactly when the entity did match the destructor's component types
|
||||||
* before detaching components, and doesn't match them after detaching.
|
* before detaching components, and doesn't match them after detaching.
|
||||||
|
|
@ -239,6 +262,9 @@ namespace psemek::ecs
|
||||||
*
|
*
|
||||||
* Note that there is no way to cancel the entity's destruction.
|
* Note that there is no way to cancel the entity's destruction.
|
||||||
*
|
*
|
||||||
|
* The destructor is not considered to be a modification of the entity, i.e. it
|
||||||
|
* doesn't trigger modification callbacks.
|
||||||
|
*
|
||||||
* @param function A function to be applied to a to-be destroyed entity's components
|
* @param function A function to be applied to a to-be destroyed entity's components
|
||||||
* @return An ownerwhip token; destroying it removes the destructor from the container
|
* @return An ownerwhip token; destroying it removes the destructor from the container
|
||||||
* @warning If any two of the passed component types are equal, the call fails with
|
* @warning If any two of the passed component types are equal, the call fails with
|
||||||
|
|
@ -254,6 +280,9 @@ namespace psemek::ecs
|
||||||
* the specified set of components is modified, and the modification affects
|
* the specified set of components is modified, and the modification affects
|
||||||
* one of this callback's component types, the callback is called.
|
* one of this callback's component types, the callback is called.
|
||||||
*
|
*
|
||||||
|
* The component types can be const-qualified, in which case the corresponding function must
|
||||||
|
* also accept the corresponding components by a const reference.
|
||||||
|
*
|
||||||
* If the modification occurred via an accessor, the callback is called
|
* If the modification occurred via an accessor, the callback is called
|
||||||
* after the accessor is destroyed, allowing for transaction-like modification.
|
* after the accessor is destroyed, allowing for transaction-like modification.
|
||||||
*
|
*
|
||||||
|
|
@ -295,16 +324,16 @@ namespace psemek::ecs
|
||||||
template <typename ... Components>
|
template <typename ... Components>
|
||||||
handle container::create(Components && ... components)
|
handle container::create(Components && ... components)
|
||||||
{
|
{
|
||||||
static_assert(detail::all_different_types_v<Components...>, "all component types must be different");
|
static_assert(detail::all_different_types_v<std::remove_cvref_t<Components>...>, "all component types must be different");
|
||||||
|
|
||||||
detail::component_uuid_helper<Components...> uuids;
|
detail::component_uuid_helper<std::remove_cvref_t<Components>...> uuids;
|
||||||
|
|
||||||
auto table = table_container_.get(uuids.get());
|
auto table = table_container_.get(uuids.get());
|
||||||
|
|
||||||
if (!table)
|
if (!table)
|
||||||
{
|
{
|
||||||
std::vector<std::unique_ptr<detail::column>> columns;
|
std::vector<std::unique_ptr<detail::column>> columns;
|
||||||
(columns.push_back(std::make_unique<detail::column_impl<Components>>()), ...);
|
(columns.push_back(std::make_unique<detail::column_impl<std::remove_cvref_t<Components>>>()), ...);
|
||||||
|
|
||||||
table = insert_table(std::move(columns));
|
table = insert_table(std::move(columns));
|
||||||
}
|
}
|
||||||
|
|
@ -317,7 +346,7 @@ namespace psemek::ecs
|
||||||
[[maybe_unused]] accessor accessor = get(handle);
|
[[maybe_unused]] accessor accessor = get(handle);
|
||||||
|
|
||||||
table->push_row(handle);
|
table->push_row(handle);
|
||||||
((accessor.get<Components>() = std::move(components)), ...);
|
((accessor.get<std::remove_cvref_t<Components>>() = std::forward<Components>(components)), ...);
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
@ -325,13 +354,13 @@ namespace psemek::ecs
|
||||||
template <typename ... Components>
|
template <typename ... Components>
|
||||||
void container::attach(handle const & entity, Components && ... components)
|
void container::attach(handle const & entity, Components && ... components)
|
||||||
{
|
{
|
||||||
static_assert(detail::all_different_types_v<Components...>, "all component types must be different");
|
static_assert(detail::all_different_types_v<std::remove_cvref_t<Components>...>, "all component types must be different");
|
||||||
|
|
||||||
auto & data = entity_list_.get_entities()[entity.id];
|
auto & data = entity_list_.get_entities()[entity.id];
|
||||||
for (auto const & column : data.table->columns())
|
for (auto const & column : data.table->columns())
|
||||||
uuid_helper_.push_back(column->uuid());
|
uuid_helper_.push_back(column->uuid());
|
||||||
|
|
||||||
((data.table->column(Components::uuid()) ? 0 : (uuid_helper_.push_back(Components::uuid()), 0)), ...);
|
((data.table->column(std::remove_cvref_t<Components>::uuid()) ? 0 : (uuid_helper_.push_back(std::remove_cvref_t<Components>::uuid()), 0)), ...);
|
||||||
|
|
||||||
auto table = table_container_.get(uuid_helper_);
|
auto table = table_container_.get(uuid_helper_);
|
||||||
|
|
||||||
|
|
@ -341,7 +370,7 @@ namespace psemek::ecs
|
||||||
for (auto const & column : data.table->columns())
|
for (auto const & column : data.table->columns())
|
||||||
columns.push_back(column->clone());
|
columns.push_back(column->clone());
|
||||||
|
|
||||||
((data.table->column(Components::uuid()) ? 0 : (columns.push_back(std::make_unique<detail::column_impl<Components>>()), 0)), ...);
|
((data.table->column(std::remove_cvref_t<Components>::uuid()) ? 0 : (columns.push_back(std::make_unique<detail::column_impl<std::remove_cvref_t<Components>>>()), 0)), ...);
|
||||||
|
|
||||||
table = insert_table(std::move(columns));
|
table = insert_table(std::move(columns));
|
||||||
}
|
}
|
||||||
|
|
@ -359,7 +388,7 @@ namespace psemek::ecs
|
||||||
}
|
}
|
||||||
|
|
||||||
auto accessor = get(entity);
|
auto accessor = get(entity);
|
||||||
((accessor.get<Components>() = std::move(components)), ...);
|
((accessor.get<std::remove_cvref_t<Components>>() = std::forward<Components>(components)), ...);
|
||||||
|
|
||||||
uuid_helper_.clear();
|
uuid_helper_.clear();
|
||||||
}
|
}
|
||||||
|
|
@ -367,9 +396,9 @@ namespace psemek::ecs
|
||||||
template <typename ... Components>
|
template <typename ... Components>
|
||||||
void container::detach(handle const & entity)
|
void container::detach(handle const & entity)
|
||||||
{
|
{
|
||||||
static_assert(detail::all_different_types_v<Components...>, "all component types must be different");
|
static_assert(detail::all_different_types_v<std::remove_cvref_t<Components>...>, "all component types must be different");
|
||||||
|
|
||||||
(uuid_set_helper_.insert(Components::uuid()), ...);
|
(uuid_set_helper_.insert(std::remove_cvref_t<Components>::uuid()), ...);
|
||||||
|
|
||||||
auto & data = entity_list_.get_entities()[entity.id];
|
auto & data = entity_list_.get_entities()[entity.id];
|
||||||
for (auto const & column : data.table->columns())
|
for (auto const & column : data.table->columns())
|
||||||
|
|
@ -407,7 +436,7 @@ namespace psemek::ecs
|
||||||
template <typename ... Components>
|
template <typename ... Components>
|
||||||
query_cache container::cache()
|
query_cache container::cache()
|
||||||
{
|
{
|
||||||
detail::component_uuid_helper<Components...> uuids;
|
detail::component_uuid_helper<std::remove_cvref_t<Components>...> uuids;
|
||||||
|
|
||||||
auto result = query_cache_container_.create(uuids.get());
|
auto result = query_cache_container_.create(uuids.get());
|
||||||
|
|
||||||
|
|
@ -421,7 +450,7 @@ namespace psemek::ecs
|
||||||
template <typename ... Components, typename Function>
|
template <typename ... Components, typename Function>
|
||||||
query_cache container::apply(Function && function, query_cache cache)
|
query_cache container::apply(Function && function, query_cache cache)
|
||||||
{
|
{
|
||||||
static_assert(detail::all_different_types_v<Components...>, "all component types must be different");
|
static_assert(detail::all_different_types_v<std::remove_const_t<Components>...>, "all component types must be different");
|
||||||
|
|
||||||
if (!cache)
|
if (!cache)
|
||||||
cache = this->cache<Components...>();
|
cache = this->cache<Components...>();
|
||||||
|
|
@ -465,7 +494,7 @@ namespace psemek::ecs
|
||||||
template <typename ... Components, typename Function>
|
template <typename ... Components, typename Function>
|
||||||
query_cache container::batch_apply(Function && function, query_cache cache)
|
query_cache container::batch_apply(Function && function, query_cache cache)
|
||||||
{
|
{
|
||||||
static_assert(detail::all_different_types_v<Components...>, "all component types must be different");
|
static_assert(detail::all_different_types_v<std::remove_const_t<Components>...>, "all component types must be different");
|
||||||
|
|
||||||
if (!cache)
|
if (!cache)
|
||||||
cache = this->cache<Components...>();
|
cache = this->cache<Components...>();
|
||||||
|
|
|
||||||
|
|
@ -261,3 +261,15 @@ test_case(ecs_apply_create)
|
||||||
});
|
});
|
||||||
expect_equal(call_count, 2 * count);
|
expect_equal(call_count, 2 * count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_case(ecs_apply_const)
|
||||||
|
{
|
||||||
|
container container;
|
||||||
|
|
||||||
|
container.create(component_1{10}, component_2{20});
|
||||||
|
container.apply<component_1, component_2 const>([](component_1 & value1, component_2 const & value2){
|
||||||
|
expect_equal(value1.value, 10);
|
||||||
|
expect_equal(value2.value, 20);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue