diff --git a/TODO b/TODO index 870ecf783..0b7e6d01b 100644 --- a/TODO +++ b/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 diff --git a/docs/md/meta.md b/docs/md/meta.md index 1ee899d90..93866b1ff 100644 --- a/docs/md/meta.md +++ b/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`.
+ 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{}.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. diff --git a/src/entt/meta/policy.hpp b/src/entt/meta/policy.hpp index b939599de..4d7f1139d 100644 --- a/src/entt/meta/policy.hpp +++ b/src/entt/meta/policy.hpp @@ -5,24 +5,16 @@ namespace entt { -/*! @brief Empty class type used to request the _as ref_ policy. */ -struct as_ref_t final { - /*! @cond TURN_OFF_DOXYGEN */ - template - static constexpr bool value = std::is_reference_v && !std::is_const_v>; - /*! @endcond */ -}; +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { -/*! @brief Empty class type used to request the _as cref_ policy. */ -struct as_cref_t final { - /*! @cond TURN_OFF_DOXYGEN */ - template - static constexpr bool value = std::is_reference_v; - /*! @endcond */ -}; +struct meta_policy {}; + +} // namespace internal +/*! @endcond */ /*! @brief Empty class type used to request the _as-is_ policy. */ -struct as_is_t final { +struct as_is_t final: private internal::meta_policy { /*! @cond TURN_OFF_DOXYGEN */ template static constexpr bool value = true; @@ -30,7 +22,31 @@ struct as_is_t final { }; /*! @brief Empty class type used to request the _as void_ policy. */ -struct as_void_t final { +struct as_void_t final: private internal::meta_policy { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as ref_ policy. */ +struct as_ref_t final: private internal::meta_policy { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v && !std::is_const_v>; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as cref_ policy. */ +struct as_cref_t final: private internal::meta_policy { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v; + /*! @endcond */ +}; + +/*! @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 static constexpr bool value = true; @@ -44,7 +60,7 @@ struct as_void_t final { */ template struct is_meta_policy - : std::bool_constant || std::is_same_v || std::is_same_v || std::is_same_v> {}; + : std::bool_constant> {}; /** * @brief Helper variable template. diff --git a/src/entt/meta/utility.hpp b/src/entt/meta/utility.hpp index 35f90dd77..f7c55d03f 100644 --- a/src/entt/meta/utility.hpp +++ b/src/entt/meta/utility.hpp @@ -166,13 +166,13 @@ using meta_function_helper_t = typename meta_function_helper::t */ template [[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { - if constexpr(std::is_same_v) { - return meta_any{ctx, std::in_place_type}; - } else if constexpr(std::is_same_v) { - return meta_any{ctx, std::in_place_type, value}; - } else if constexpr(std::is_same_v) { + if constexpr(std::is_same_v) { static_assert(std::is_lvalue_reference_v, "Invalid type"); return meta_any{ctx, std::in_place_type &>, std::as_const(value)}; + } else if constexpr(std::is_same_v || (std::is_same_v && std::is_lvalue_reference_v)) { + return meta_any{ctx, std::in_place_type, value}; + } else if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type}; } else { return meta_any{ctx, std::forward(value)}; } diff --git a/test/entt/meta/meta_data.cpp b/test/entt/meta/meta_data.cpp index eebcb8f3d..28af54968 100644 --- a/test/entt/meta/meta_data.cpp +++ b/test/entt/meta/meta_data.cpp @@ -97,6 +97,13 @@ struct MetaData: ::testing::Test { .data<&clazz::i, entt::as_void_t>("void"_hs) .conv(); + entt::meta_factory{} + .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("ao"_hs); + entt::meta_factory{} .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}); } +TEST_F(MetaData, AsAuto) { + using namespace entt::literals; + + auto type = entt::resolve(); + entt::meta_data data{}; + clazz instance{}; + + data = type.data("ai"_hs); + + ASSERT_TRUE(data); + ASSERT_EQ(data.type(), entt::resolve()); + 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()); + 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()); + 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()); + 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()); + ASSERT_EQ(data.get(instance).base().policy(), entt::any_policy::embedded); +} + TEST_F(MetaData, AsRef) { using namespace entt::literals; diff --git a/test/entt/meta/meta_func.cpp b/test/entt/meta/meta_func.cpp index 30cb699ae..8ea4b2cf7 100644 --- a/test/entt/meta/meta_func.cpp +++ b/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(); + + entt::meta_factory{} + .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(); + entt::meta_func func{}; + function instance{3}; + + func = type.func("aa"_hs); + + ASSERT_TRUE(func); + ASSERT_EQ(func.ret(), entt::resolve()); + 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()); + 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()); + 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()); + ASSERT_EQ(func.invoke(instance).base().policy(), entt::any_policy::empty); +} + TEST_F(MetaFunc, AsRef) { using namespace entt::literals; diff --git a/test/entt/meta/meta_utility.cpp b/test/entt/meta/meta_utility.cpp index e8cab8cf5..683f71a0a 100644 --- a/test/entt/meta/meta_utility.cpp +++ b/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(value); - auto as_ref = entt::meta_dispatch(value); auto as_cref = entt::meta_dispatch(value); + auto as_ref = entt::meta_dispatch(value); + auto as_void = entt::meta_dispatch(value); + auto as_auto_copy = entt::meta_dispatch(static_cast(value)); + auto as_auto_cref = entt::meta_dispatch(std::as_const(value)); + auto as_auto_ref = entt::meta_dispatch(value); auto as_is = entt::meta_dispatch(value); - ASSERT_EQ(as_void.type(), entt::resolve()); - ASSERT_EQ(as_ref.type(), entt::resolve()); ASSERT_EQ(as_cref.type(), entt::resolve()); + ASSERT_EQ(as_ref.type(), entt::resolve()); + ASSERT_EQ(as_void.type(), entt::resolve()); + ASSERT_EQ(as_auto_copy.type(), entt::resolve()); + ASSERT_EQ(as_auto_cref.type(), entt::resolve()); + ASSERT_EQ(as_auto_ref.type(), entt::resolve()); ASSERT_EQ(as_is.type(), entt::resolve()); - ASSERT_NE(as_is.try_cast(), nullptr); - ASSERT_NE(as_ref.try_cast(), 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(), nullptr); ASSERT_NE(as_cref.try_cast(), nullptr); + ASSERT_NE(as_ref.try_cast(), nullptr); + ASSERT_NE(as_auto_copy.try_cast(), nullptr); + ASSERT_EQ(as_auto_cref.try_cast(), nullptr); + ASSERT_NE(as_auto_ref.try_cast(), nullptr); + ASSERT_NE(as_is.try_cast(), nullptr); - ASSERT_EQ(as_is.cast(), 2); - ASSERT_EQ(as_ref.cast(), 2); ASSERT_EQ(as_cref.cast(), 2); + ASSERT_EQ(as_ref.cast(), 2); + ASSERT_EQ(as_auto_copy.cast(), 2); + ASSERT_EQ(as_auto_cref.cast(), 2); + ASSERT_EQ(as_auto_ref.cast(), 2); + ASSERT_EQ(as_is.cast(), 2); } TEST_F(MetaUtility, MetaDispatchMetaAny) {