more on context variables

This commit is contained in:
Michele Caini
2019-03-25 15:21:13 +01:00
parent 8ef0c66966
commit 2147436a2a
4 changed files with 60 additions and 26 deletions

2
TODO
View File

@@ -20,5 +20,5 @@
- each components only return actual component, so shared components are returned only once
* types defined at runtime that refer to the same compile-time type (but to different pools) are possible, the library is almost there
* add take functionality, eg registry.take(entity, other); where it takes the entity and all its components from registry and move them to other
* what about paged pools? vector of fixed-size blocks (ease shared components, multi-ownership, etc).
* add entity function to views/groups (component -> owner, see sparse sets)
* what about paged pools? vector of fixed-size blocks

View File

@@ -831,8 +831,9 @@ const bool null = (entity == entt::null);
It is often convenient to assign context variables to a registry, so as to make
it the only _source of truth_ of an application.<br/>
This is possible by means of a member function named `set` to use to create a
context variable from a given type. Later on, `ctx` can be used to retrieve the
newly created instance and `unset` is there to literally reset it if needed.
context variable from a given type. Later on, either `ctx` or `try_ctx` can be
used to retrieve the newly created instance and `unset` is there to literally
reset it if needed.
Example of use:
@@ -840,7 +841,11 @@ Example of use:
// creates a new context variable initialized with the given values
registry.set<my_type>(42, 'c');
if(auto *var = registry.ctx<my_type>(); var) {
// gets the context variable
const auto &var = registry.ctx<my_type>();
// if in doubts, probe the registry to avoid assertions in case of errors
if(auto *ptr = registry.try_ctx<my_type>(); var) {
// uses the context variable associated with a registry, if any
}
@@ -850,9 +855,10 @@ registry.unset<my_type>();
The type of a context variable must be such that it's default constructible and
can be moved. The `set` member function either creates a new instance of the
context variable or overwrites an already existing one if any. The `ctx` member
function returns a pointer to the context variable if it exists, otherwise it
returns a null pointer. This fits well with the `if` statement with initializer.
context variable or overwrites an already existing one if any. The `try_ctx`
member function returns a pointer to the context variable if it exists,
otherwise it returns a null pointer. This fits well with the `if` statement with
initializer.
# Views and Groups

View File

@@ -1645,7 +1645,7 @@ public:
* registry, a null pointer otherwise.
*/
template<typename Type>
const Type * ctx() const ENTT_NOEXCEPT {
const Type * try_ctx() const ENTT_NOEXCEPT {
const auto ctype = runtime_type<Type, context_family>();
if constexpr(is_named_type_v<Type>) {
@@ -1660,10 +1660,35 @@ public:
}
}
/*! @copydoc try_ctx */
template<typename Type>
inline Type * try_ctx() ENTT_NOEXCEPT {
return const_cast<Type *>(std::as_const(*this).template try_ctx<Type>());
}
/**
* @brief Returns a reference to an object in the context of the registry.
*
* @warning
* Attempting to get a context variable that doesn't exist results in
* undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode in case of
* invalid requests.
*
* @tparam Type Type of object to get.
* @return A valid reference to the object in the context of the registry.
*/
template<typename Type>
const Type & ctx() const ENTT_NOEXCEPT {
const auto *instance = try_ctx<Type>();
assert(instance);
return *instance;
}
/*! @copydoc ctx */
template<typename Type>
Type * ctx() ENTT_NOEXCEPT {
return const_cast<Type *>(std::as_const(*this).template ctx<Type>());
inline Type & ctx() ENTT_NOEXCEPT {
return const_cast<Type &>(std::as_const(*this).template ctx<Type>());
}
private:

View File

@@ -35,42 +35,45 @@ struct listener {
TEST(Registry, Context) {
entt::registry registry;
ASSERT_EQ(registry.ctx<char>(), nullptr);
ASSERT_EQ(registry.ctx<int>(), nullptr);
ASSERT_EQ(registry.ctx<double>(), nullptr);
ASSERT_EQ(registry.try_ctx<char>(), nullptr);
ASSERT_EQ(registry.try_ctx<int>(), nullptr);
ASSERT_EQ(registry.try_ctx<double>(), nullptr);
registry.set<char>();
registry.set<int>();
registry.set<double>();
ASSERT_NE(registry.ctx<char>(), nullptr);
ASSERT_NE(registry.ctx<int>(), nullptr);
ASSERT_NE(registry.ctx<double>(), nullptr);
ASSERT_NE(registry.try_ctx<char>(), nullptr);
ASSERT_NE(registry.try_ctx<int>(), nullptr);
ASSERT_NE(registry.try_ctx<double>(), nullptr);
registry.unset<int>();
registry.unset<double>();
ASSERT_NE(registry.ctx<char>(), nullptr);
ASSERT_EQ(registry.ctx<int>(), nullptr);
ASSERT_EQ(registry.ctx<double>(), nullptr);
ASSERT_NE(registry.try_ctx<char>(), nullptr);
ASSERT_EQ(registry.try_ctx<int>(), nullptr);
ASSERT_EQ(registry.try_ctx<double>(), nullptr);
registry.set<char>('c');
registry.set<int>(42);
registry.set<double>(1.);
ASSERT_EQ(*registry.ctx<char>(), 'c');
ASSERT_NE(registry.ctx<char>(), nullptr);
ASSERT_EQ(registry.ctx<char>(), 'c');
ASSERT_NE(registry.try_ctx<char>(), nullptr);
ASSERT_EQ(registry.try_ctx<char>(), &registry.ctx<char>());
ASSERT_EQ(registry.ctx<char>(), std::as_const(registry).ctx<char>());
ASSERT_EQ(*registry.ctx<int>(), 42);
ASSERT_NE(registry.ctx<int>(), nullptr);
ASSERT_EQ(registry.ctx<int>(), 42);
ASSERT_NE(registry.try_ctx<int>(), nullptr);
ASSERT_EQ(registry.try_ctx<int>(), &registry.ctx<int>());
ASSERT_EQ(registry.ctx<int>(), std::as_const(registry).ctx<int>());
ASSERT_EQ(*registry.ctx<double>(), 1.);
ASSERT_NE(registry.ctx<double>(), nullptr);
ASSERT_EQ(registry.ctx<double>(), 1.);
ASSERT_NE(registry.try_ctx<double>(), nullptr);
ASSERT_EQ(registry.try_ctx<double>(), &registry.ctx<double>());
ASSERT_EQ(registry.ctx<double>(), std::as_const(registry).ctx<double>());
ASSERT_EQ(registry.ctx<float>(), nullptr);
ASSERT_EQ(registry.try_ctx<float>(), nullptr);
}
TEST(Registry, Types) {