Просмотр исходного кода

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

Michele Caini 5 лет назад
Родитель
Сommit
b18b76c1f0
3 измененных файлов с 28 добавлено и 21 удалено
  1. 13 16
      docs/md/meta.md
  2. 8 4
      src/entt/meta/factory.hpp
  3. 7 1
      test/entt/meta/meta_data.cpp

+ 13 - 16
docs/md/meta.md

@@ -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);
 ```

+ 8 - 4
src/entt/meta/factory.hpp

@@ -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 {

+ 7 - 1
test/entt/meta/meta_data.cpp

@@ -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));