meta: as_ref_t adapts to const objects/const-ref return types

This commit is contained in:
Michele Caini
2021-01-22 11:41:17 +01:00
parent c9f65267ba
commit b18b76c1f0
3 changed files with 28 additions and 21 deletions

View File

@@ -712,33 +712,30 @@ There are a few alternatives available at the moment:
* The _as-void_ policy, associated with the type `entt::as_void_t`.<br/>
Its purpose is to discard the return value of a meta object, whatever it is,
thus making it appear as if its type were `void`.<br/>
thus making it appear as if its type were `void`:
```cpp
entt::meta<my_type>().func<&my_type::member_function, entt::as_void_t>("member"_hs);
```
If the use with functions is obvious, it must be said that it's also possible
to use this policy with constructors and data members. In the first case, the
constructor will be invoked but the returned wrapper will actually be empty.
In the second case, instead, the property will not be accessible for reading.
As an example of use:
```cpp
entt::meta<my_type>().func<&my_type::member_function, entt::as_void_t>("member"_hs);
```
* The _as-ref_ and _as-cref_ policies, associated with the types
`entt::as_ref_t` and `entt::as_cref_t`.<br/>
They allow to build wrappers that act as references to unmanaged objects.
Accessing the object contained in the wrapper for which the _reference_ was
requested will make it possible to directly access the instance used to
initialize the wrapper itself.<br/>
These policies work with constructors (for example, when objects are taken
from an external container rather than created on demand), data members and
functions in general (as long as their return types are lvalue references).
As an example of use:
initialize the wrapper itself:
```cpp
entt::meta<my_type>().data<&my_type::data_member, entt::as_ref_t>("member"_hs);
```
These policies work with constructors (for example, when objects are taken
from an external container rather than created on demand), data members and
functions in general.<br/>
If on the one hand `as_cref_t` always forces the return type to be const,
`as_ref_t` _adapts_ to the constness of the passed object and to that of the
return type if any.
Some uses are rather trivial, but it's useful to note that there are some less
obvious corner cases that can in turn be solved with the use of policies.
@@ -763,8 +760,8 @@ Exporting constant values or elements from an enum is as simple as ever:
```cpp
entt::meta<my_enum>()
.data<my_enum::a_value>("a_value"_hs)
.data<my_enum::another_value>("another_value"_hs);
.data<my_enum::a_value>("a_value"_hs)
.data<my_enum::another_value>("another_value"_hs);
entt::meta<int>().data<2048>("max_int"_hs);
```

View File

@@ -123,7 +123,7 @@ template<typename Type, auto Data, typename Policy>
if constexpr(std::is_same_v<Policy, as_void_t>) {
return meta_any{std::in_place_type<void>, std::forward<decltype(value)>(value)};
} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
return meta_any{std::ref(std::forward<decltype(value)>(value))};
return meta_any{std::reference_wrapper{std::forward<decltype(value)>(value)}};
} else if constexpr(std::is_same_v<Policy, as_cref_t>) {
return meta_any{std::cref(std::forward<decltype(value)>(value))};
} else {
@@ -139,8 +139,12 @@ template<typename Type, auto Data, typename Policy>
if constexpr(std::is_array_v<std::remove_cv_t<std::remove_reference_t<decltype(std::declval<Type>().*Data)>>>) {
return meta_any{};
} else {
auto * const clazz = instance->try_cast<std::conditional_t<std::is_same_v<Policy, as_ref_t>, Type, const Type>>();
return clazz ? dispatch(std::invoke(Data, clazz)) : meta_any{};
if(auto * clazz = instance->try_cast<Type>(); clazz) {
return dispatch(std::invoke(Data, *clazz));
} else {
auto * fallback = instance->try_cast<const Type>();
return fallback ? dispatch(std::invoke(Data, *fallback)) : meta_any{};
}
}
} else if constexpr(std::is_pointer_v<std::decay_t<decltype(Data)>>) {
if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) {
@@ -163,7 +167,7 @@ template<typename Type, auto Candidate, typename Policy, std::size_t... Index>
std::invoke(Candidate, std::forward<decltype(params)>(params)...);
return meta_any{std::in_place_type<void>};
} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
return meta_any{std::ref(std::invoke(Candidate, std::forward<decltype(params)>(params)...))};
return meta_any{std::reference_wrapper{std::invoke(Candidate, std::forward<decltype(params)>(params)...)}};
} else if constexpr(std::is_same_v<Policy, as_cref_t>) {
return meta_any{std::cref(std::invoke(Candidate, std::forward<decltype(params)>(params)...))};
} else {

View File

@@ -418,9 +418,15 @@ TEST_F(MetaData, ConstInstance) {
clazz_t instance{};
ASSERT_NE(entt::resolve<clazz_t>().data("i"_hs).get(instance).try_cast<int>(), nullptr);
ASSERT_NE(entt::resolve<clazz_t>().data("i"_hs).get(instance).try_cast<const int>(), nullptr);
ASSERT_EQ(entt::resolve<clazz_t>().data("i"_hs).get(std::as_const(instance)).try_cast<int>(), nullptr);
// as_ref_t adapts to the constness of the passed object and returns const references in case
ASSERT_NE(entt::resolve<clazz_t>().data("i"_hs).get(std::as_const(instance)).try_cast<const int>(), nullptr);
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).get(instance));
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(instance, 3));
ASSERT_FALSE(entt::resolve<clazz_t>().data("i"_hs).get(std::as_const(instance)));
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).get(std::as_const(instance)));
ASSERT_FALSE(entt::resolve<clazz_t>().data("i"_hs).set(std::as_const(instance), 3));
ASSERT_TRUE(entt::resolve<clazz_t>().data("ci"_hs).get(instance));