skypjack пре 4 месеци
родитељ
комит
d366a5ed92

+ 1 - 1
TODO

@@ -33,6 +33,6 @@ TODO:
 * resource: shared_from_this?
 * finish the imgui viewer/editor!
 * introduce compile-time flag to skip dll-only checks in basic_any
-* auto policy for return types in meta (i.e. it picks as_is or as_(c)ref automatically as needed)
 * find a way to test deleter and the absence of it in basic_any
 * archetype-like a-là EnTT support (see my own notes)
+* rename meta policy as_is in as_copy

+ 12 - 0
docs/md/meta.md

@@ -825,6 +825,18 @@ There are a few alternatives available at the moment:
   `as_ref_t` _adapts_ to the constness of the passed object and to that of the
   return type if any.
 
+* The _as-auto_ policy, associated with the type `entt::as_auto_t`.<br/>
+  Useful for decoupling meta type creation code from calling code while still
+  preserving the behavior of data members and member functions as defined:
+
+  ```cpp
+  entt::meta_factory<my_type>{}.func<&my_type::any_member, entt::as_auto_t>("member"_hs);
+  ```
+
+  For data members or member functions that return a reference type, the value
+  is returned by reference with the same constness. In all other cases, the
+  value is returned by copy.
+
 Some uses are rather trivial, but it is useful to note that there are some less
 obvious corner cases that can in turn be solved with the use of policies.
 

+ 29 - 13
src/entt/meta/policy.hpp

@@ -5,8 +5,32 @@
 
 namespace entt {
 
+/*! @cond TURN_OFF_DOXYGEN */
+namespace internal {
+
+struct meta_policy {};
+
+} // namespace internal
+/*! @endcond */
+
+/*! @brief Empty class type used to request the _as-is_ policy. */
+struct as_is_t final: private internal::meta_policy {
+    /*! @cond TURN_OFF_DOXYGEN */
+    template<typename>
+    static constexpr bool value = true;
+    /*! @endcond */
+};
+
+/*! @brief Empty class type used to request the _as void_ policy. */
+struct as_void_t final: private internal::meta_policy {
+    /*! @cond TURN_OFF_DOXYGEN */
+    template<typename>
+    static constexpr bool value = true;
+    /*! @endcond */
+};
+
 /*! @brief Empty class type used to request the _as ref_ policy. */
-struct as_ref_t final {
+struct as_ref_t final: private internal::meta_policy {
     /*! @cond TURN_OFF_DOXYGEN */
     template<typename Type>
     static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>;
@@ -14,23 +38,15 @@ struct as_ref_t final {
 };
 
 /*! @brief Empty class type used to request the _as cref_ policy. */
-struct as_cref_t final {
+struct as_cref_t final: private internal::meta_policy {
     /*! @cond TURN_OFF_DOXYGEN */
     template<typename Type>
     static constexpr bool value = std::is_reference_v<Type>;
     /*! @endcond */
 };
 
-/*! @brief Empty class type used to request the _as-is_ policy. */
-struct as_is_t final {
-    /*! @cond TURN_OFF_DOXYGEN */
-    template<typename>
-    static constexpr bool value = true;
-    /*! @endcond */
-};
-
-/*! @brief Empty class type used to request the _as void_ policy. */
-struct as_void_t final {
+/*! @brief Empty class type used to request the _as auto_ policy. */
+struct as_auto_t final: private internal::meta_policy {
     /*! @cond TURN_OFF_DOXYGEN */
     template<typename>
     static constexpr bool value = true;
@@ -44,7 +60,7 @@ struct as_void_t final {
  */
 template<typename Type>
 struct is_meta_policy
-    : std::bool_constant<std::is_same_v<Type, as_ref_t> || std::is_same_v<Type, as_cref_t> || std::is_same_v<Type, as_is_t> || std::is_same_v<Type, as_void_t>> {};
+    : std::bool_constant<std::is_base_of_v<internal::meta_policy, Type>> {};
 
 /**
  * @brief Helper variable template.

+ 5 - 5
src/entt/meta/utility.hpp

@@ -166,13 +166,13 @@ using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::t
  */
 template<typename Policy = as_is_t, typename Type>
 [[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) {
-    if constexpr(std::is_same_v<Policy, as_void_t>) {
-        return meta_any{ctx, std::in_place_type<void>};
-    } else if constexpr(std::is_same_v<Policy, as_ref_t>) {
-        return meta_any{ctx, std::in_place_type<Type>, value};
-    } else if constexpr(std::is_same_v<Policy, as_cref_t>) {
+    if constexpr(std::is_same_v<Policy, as_cref_t>) {
         static_assert(std::is_lvalue_reference_v<Type>, "Invalid type");
         return meta_any{ctx, std::in_place_type<const std::remove_reference_t<Type> &>, std::as_const(value)};
+    } else if constexpr(std::is_same_v<Policy, as_ref_t> || (std::is_same_v<Policy, as_auto_t> && std::is_lvalue_reference_v<Type>)) {
+        return meta_any{ctx, std::in_place_type<Type>, value};
+    } else if constexpr(std::is_same_v<Policy, as_void_t>) {
+        return meta_any{ctx, std::in_place_type<void>};
     } else {
         return meta_any{ctx, std::forward<Type>(value)};
     }

+ 45 - 0
test/entt/meta/meta_data.cpp

@@ -97,6 +97,13 @@ struct MetaData: ::testing::Test {
             .data<&clazz::i, entt::as_void_t>("void"_hs)
             .conv<int>();
 
+        entt::meta_factory<clazz>{}
+            .data<&clazz::i, entt::as_auto_t>("ai"_hs)
+            .data<&clazz::j, entt::as_auto_t>("aj"_hs)
+            .data<&clazz::h, entt::as_auto_t>("ah"_hs)
+            .data<&clazz::k, entt::as_auto_t>("ak"_hs)
+            .data<nullptr, &clazz::operator int, entt::as_auto_t>("ao"_hs);
+
         entt::meta_factory<setter_getter>{}
             .type("setter_getter"_hs)
             .data<&setter_getter::static_setter, &setter_getter::static_getter>("x"_hs)
@@ -541,6 +548,44 @@ TEST_F(MetaData, AsVoid) {
     ASSERT_EQ(data.get(instance), entt::meta_any{std::in_place_type<void>});
 }
 
+TEST_F(MetaData, AsAuto) {
+    using namespace entt::literals;
+
+    auto type = entt::resolve<clazz>();
+    entt::meta_data data{};
+    clazz instance{};
+
+    data = type.data("ai"_hs);
+
+    ASSERT_TRUE(data);
+    ASSERT_EQ(data.type(), entt::resolve<int>());
+    ASSERT_EQ(data.get(instance).base().policy(), entt::any_policy::ref);
+
+    data = type.data("aj"_hs);
+
+    ASSERT_TRUE(data);
+    ASSERT_EQ(data.type(), entt::resolve<int>());
+    ASSERT_EQ(data.get(instance).base().policy(), entt::any_policy::cref);
+
+    data = type.data("ah"_hs);
+
+    ASSERT_TRUE(data);
+    ASSERT_EQ(data.type(), entt::resolve<int>());
+    ASSERT_EQ(data.get(instance).base().policy(), entt::any_policy::ref);
+
+    data = type.data("ak"_hs);
+
+    ASSERT_TRUE(data);
+    ASSERT_EQ(data.type(), entt::resolve<int>());
+    ASSERT_EQ(data.get(instance).base().policy(), entt::any_policy::cref);
+
+    data = type.data("ao"_hs);
+
+    ASSERT_TRUE(data);
+    ASSERT_EQ(data.type(), entt::resolve<int>());
+    ASSERT_EQ(data.get(instance).base().policy(), entt::any_policy::embedded);
+}
+
 TEST_F(MetaData, AsRef) {
     using namespace entt::literals;
 

+ 39 - 1
test/entt/meta/meta_func.cpp

@@ -74,7 +74,7 @@ struct function {
         instance.value = iv;
     }
 
-    [[nodiscard]] int v(int &iv) const {
+    [[nodiscard]] const int &v(int &iv) const {
         return (iv = value);
     }
 
@@ -137,6 +137,12 @@ struct MetaFunc: ::testing::Test {
             .func<&function::a, entt::as_ref_t>("a"_hs)
             .func<&function::a, entt::as_cref_t>("ca"_hs)
             .conv<int>();
+
+        entt::meta_factory<function>{}
+            .func<&function::a, entt::as_auto_t>("aa"_hs)
+            .func<&function::v, entt::as_auto_t>("av"_hs)
+            .func<&function::operator int, entt::as_auto_t>("ao"_hs)
+            .func<&function::g, entt::as_auto_t>("ag"_hs);
     }
 
     void TearDown() override {
@@ -503,6 +509,38 @@ TEST_F(MetaFunc, AsVoid) {
     ASSERT_EQ(value, instance.value);
 }
 
+TEST_F(MetaFunc, AsAuto) {
+    using namespace entt::literals;
+
+    auto type = entt::resolve<function>();
+    entt::meta_func func{};
+    function instance{3};
+
+    func = type.func("aa"_hs);
+
+    ASSERT_TRUE(func);
+    ASSERT_EQ(func.ret(), entt::resolve<int>());
+    ASSERT_EQ(func.invoke(instance).base().policy(), entt::any_policy::ref);
+
+    func = type.func("av"_hs);
+
+    ASSERT_TRUE(func);
+    ASSERT_EQ(func.ret(), entt::resolve<int>());
+    ASSERT_EQ(func.invoke(instance, 3).base().policy(), entt::any_policy::cref);
+
+    func = type.func("ao"_hs);
+
+    ASSERT_TRUE(func);
+    ASSERT_EQ(func.ret(), entt::resolve<int>());
+    ASSERT_EQ(func.invoke(instance).base().policy(), entt::any_policy::embedded);
+
+    func = type.func("ag"_hs);
+
+    ASSERT_TRUE(func);
+    ASSERT_EQ(func.ret(), entt::resolve<void>());
+    ASSERT_EQ(func.invoke(instance).base().policy(), entt::any_policy::empty);
+}
+
 TEST_F(MetaFunc, AsRef) {
     using namespace entt::literals;
 

+ 28 - 8
test/entt/meta/meta_utility.cpp

@@ -59,24 +59,44 @@ using MetaUtilityDeathTest = MetaUtility;
 TEST_F(MetaUtility, MetaDispatch) {
     int value = 2;
 
-    auto as_void = entt::meta_dispatch<entt::as_void_t>(value);
-    auto as_ref = entt::meta_dispatch<entt::as_ref_t>(value);
     auto as_cref = entt::meta_dispatch<entt::as_cref_t>(value);
+    auto as_ref = entt::meta_dispatch<entt::as_ref_t>(value);
+    auto as_void = entt::meta_dispatch<entt::as_void_t>(value);
+    auto as_auto_copy = entt::meta_dispatch<entt::as_auto_t>(static_cast<int &&>(value));
+    auto as_auto_cref = entt::meta_dispatch<entt::as_auto_t>(std::as_const(value));
+    auto as_auto_ref = entt::meta_dispatch<entt::as_auto_t>(value);
     auto as_is = entt::meta_dispatch(value);
 
-    ASSERT_EQ(as_void.type(), entt::resolve<void>());
-    ASSERT_EQ(as_ref.type(), entt::resolve<int>());
     ASSERT_EQ(as_cref.type(), entt::resolve<int>());
+    ASSERT_EQ(as_ref.type(), entt::resolve<int>());
+    ASSERT_EQ(as_void.type(), entt::resolve<void>());
+    ASSERT_EQ(as_auto_copy.type(), entt::resolve<int>());
+    ASSERT_EQ(as_auto_cref.type(), entt::resolve<int>());
+    ASSERT_EQ(as_auto_ref.type(), entt::resolve<int>());
     ASSERT_EQ(as_is.type(), entt::resolve<int>());
 
-    ASSERT_NE(as_is.try_cast<int>(), nullptr);
-    ASSERT_NE(as_ref.try_cast<int>(), nullptr);
+    ASSERT_EQ(as_cref.base().policy(), entt::any_policy::cref);
+    ASSERT_EQ(as_ref.base().policy(), entt::any_policy::ref);
+    ASSERT_EQ(as_void.base().policy(), entt::any_policy::empty);
+    ASSERT_EQ(as_auto_copy.base().policy(), entt::any_policy::embedded);
+    ASSERT_EQ(as_auto_cref.base().policy(), entt::any_policy::cref);
+    ASSERT_EQ(as_auto_ref.base().policy(), entt::any_policy::ref);
+    ASSERT_EQ(as_is.base().policy(), entt::any_policy::embedded);
+
     ASSERT_EQ(as_cref.try_cast<int>(), nullptr);
     ASSERT_NE(as_cref.try_cast<const int>(), nullptr);
+    ASSERT_NE(as_ref.try_cast<int>(), nullptr);
+    ASSERT_NE(as_auto_copy.try_cast<int>(), nullptr);
+    ASSERT_EQ(as_auto_cref.try_cast<int>(), nullptr);
+    ASSERT_NE(as_auto_ref.try_cast<int>(), nullptr);
+    ASSERT_NE(as_is.try_cast<int>(), nullptr);
 
-    ASSERT_EQ(as_is.cast<int>(), 2);
-    ASSERT_EQ(as_ref.cast<int>(), 2);
     ASSERT_EQ(as_cref.cast<int>(), 2);
+    ASSERT_EQ(as_ref.cast<int>(), 2);
+    ASSERT_EQ(as_auto_copy.cast<int>(), 2);
+    ASSERT_EQ(as_auto_cref.cast<int>(), 2);
+    ASSERT_EQ(as_auto_ref.cast<int>(), 2);
+    ASSERT_EQ(as_is.cast<int>(), 2);
 }
 
 TEST_F(MetaUtility, MetaDispatchMetaAny) {