From 5995adf8b6c81f2d3d983ca183bf30a6f3ffa689 Mon Sep 17 00:00:00 2001 From: Innokentiy Alaytsev Date: Mon, 7 Sep 2020 19:28:38 +0000 Subject: [PATCH] Implemented shortcuts for meta-functions and meta-data The shortcut for meta-functions allows invoking them directly from an instance of meta_type or meta_any. If the meta-function was not found or the arguments don't match its signature an invalid meta_any object is returned. The shortcut for meta-data allows getting and setting meta-data directly from an instance of meta_type of meta_any. If the meta_data was not found or getting/setting it failed and invalid meta_any object is returned. Signed-off-by: Innokentiy Alaytsev --- src/entt/meta/meta.hpp | 133 +++++++++++++++++++++++++++++++++++ test/entt/meta/meta_any.cpp | 46 ++++++++++++ test/entt/meta/meta_type.cpp | 3 + 3 files changed, 182 insertions(+) diff --git a/src/entt/meta/meta.hpp b/src/entt/meta/meta.hpp index b9f167050..e8cfc0f48 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -296,6 +296,48 @@ public: return storage.data(); } + /** + * @copybrief invoke + * + * @sa invoke + * + * @param id identifier of function to invoke. + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the new instance, if any. + */ + template + meta_any invoke(const id_type id, Args &&... args) const; + + /** + * @brief Sets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the meta + * data. Otherwise, invoking the setter results in an undefined + * behavior.
+ * The type of the value must be such that a cast or conversion to the type + * of the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param id identifier of the variable to set. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(const id_type id, Type &&value) const; + + /** + * @brief Gets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the meta + * data. Otherwise, invoking the getter results in an undefined behavior. + * + * @param id identifier of the variable to get. + * @return A meta any containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id) const; + /** * @brief Tries to cast an instance to a given type. * @tparam Type Type to which to cast the instance. @@ -1305,6 +1347,80 @@ public: return construct(arguments.data(), sizeof...(Args)); } + /** + * @brief Invokes the function with the given identifier, if possible. + * + * To invoke a meta function, the parameters must be such that a cast or + * conversion to the required types is possible. Otherwise, an empty and + * thus invalid wrapper is returned.
+ * It must be possible to cast the instance to the parent type of the meta + * function. Otherwise, invoking the underlying function results in an + * undefined behavior. + * + * @param id identifier of function to invoke. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ + [[nodiscard]] meta_any invoke(const id_type id, meta_handle instance, meta_any * const args, const std::size_t sz) const { + auto const f = func(id); + return f ? f.invoke(instance, args, sz) : meta_any{}; + } + + /** + * @copybrief invoke + * + * @sa invoke + * + * @param id identifier of function to invoke. + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the new instance, if any. + */ + template + meta_any invoke(const id_type id, meta_handle instance, Args &&... args) const { + std::array arguments{std::forward(args)...}; + return invoke(id, instance, arguments.data(), sizeof...(Args)); + } + + /** + * @brief Sets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the meta + * data. Otherwise, invoking the setter results in an undefined + * behavior.
+ * The type of the value must be such that a cast or conversion to the type + * of the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param id identifier of the variable to set. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(const id_type id, meta_handle instance, Type &&value) const { + auto const d = data(id); + return d ? d.set(std::move(instance), std::forward(value)) : false; + } + + /** + * @brief Gets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the meta + * data. Otherwise, invoking the getter results in an undefined behavior. + * + * @param id identifier of the variable to get. + * @param instance An opaque instance of the underlying type. + * @return A meta any containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { + auto const d = data(id); + return d ? d.get(std::move(instance)) : meta_any{}; + } + /** * @brief Returns a range to use to visit top-level meta properties. * @return An iterable range to use to visit top-level meta properties. @@ -1409,6 +1525,23 @@ private: } +template +meta_any meta_any::invoke(const id_type id, Args &&... args) const { + return type().invoke(id, *this, std::forward(args)...); +} + + +template +bool meta_any::set(const id_type id, Type &&value) const { + return type().set(id, *this, std::forward(value)); +} + + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) const { + return type().get(id, *this); +} + + [[nodiscard]] inline meta_type meta_base::parent() const ENTT_NOEXCEPT { return node->parent; } diff --git a/test/entt/meta/meta_any.cpp b/test/entt/meta/meta_any.cpp index a08c0470b..5dd634b48 100644 --- a/test/entt/meta/meta_any.cpp +++ b/test/entt/meta/meta_any.cpp @@ -5,6 +5,17 @@ #include #include + +struct clazz_t { + clazz_t() = default; + + void member(int i) { value = i; } + static void func() { c = 'd'; } + + static inline char c = 'c'; + int value = 0; +}; + struct empty_t { virtual ~empty_t() = default; static void destroy(empty_t &) { @@ -46,6 +57,12 @@ struct MetaAny: ::testing::Test { entt::meta().conv(); entt::meta().dtor<&empty_t::destroy>(); entt::meta().base().dtor<&fat_t::destroy>(); + + entt::meta() + .type("clazz"_hs) + .data<&clazz_t::value>("value"_hs) + .func<&clazz_t::member>("member"_hs) + .func<&clazz_t::func>("func"_hs); } void SetUp() override { @@ -622,3 +639,32 @@ TEST_F(MetaAny, UnmanageableType) { ASSERT_TRUE(std::as_const(any).convert()); ASSERT_FALSE(std::as_const(any).convert()); } + +TEST_F(MetaAny, FuncInvokeShortcut) { + clazz_t instance; + entt::meta_any any{std::ref(instance)}; + + ASSERT_TRUE(any.invoke("func"_hs)); + ASSERT_TRUE(any.invoke("member"_hs, 42)); + ASSERT_FALSE(any.invoke("non_existent"_hs, 42)); + + ASSERT_EQ(clazz_t::c, 'd'); + ASSERT_EQ(instance.value, 42); +} + +TEST_F(MetaAny, DataAccessShortcut) { + clazz_t instance; + entt::meta_any any{std::ref(instance)}; + + ASSERT_TRUE(any.set("value"_hs, 42)); + + auto const value_any = any.get("value"_hs); + + ASSERT_EQ(instance.value, 42); + ASSERT_TRUE(value_any); + ASSERT_TRUE(value_any.try_cast()); + ASSERT_EQ(value_any.cast(), 42); + + ASSERT_FALSE(any.set("non_existent"_hs, 42)); + ASSERT_FALSE(any.get("non_existent"_hs)); +} diff --git a/test/entt/meta/meta_type.cpp b/test/entt/meta/meta_type.cpp index 1c359d652..56076649b 100644 --- a/test/entt/meta/meta_type.cpp +++ b/test/entt/meta/meta_type.cpp @@ -270,6 +270,9 @@ TEST_F(MetaType, Func) { ASSERT_TRUE(type.func("func"_hs)); ASSERT_TRUE(type.func("member"_hs).invoke(instance)); ASSERT_TRUE(type.func("func"_hs).invoke({})); + + ASSERT_TRUE(type.invoke("member"_hs, instance)); + ASSERT_TRUE(type.invoke("func"_hs, {})); } TEST_F(MetaType, Construct) {