Преглед изворни кода

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 <alaitsev@gmail.com>
Innokentiy Alaytsev пре 5 година
родитељ
комит
5995adf8b6
3 измењених фајлова са 182 додато и 0 уклоњено
  1. 133 0
      src/entt/meta/meta.hpp
  2. 46 0
      test/entt/meta/meta_any.cpp
  3. 3 0
      test/entt/meta/meta_type.cpp

+ 133 - 0
src/entt/meta/meta.hpp

@@ -296,6 +296,48 @@ public:
         return storage.data();
         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<typename... Args>
+    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.<br/>
+     * 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<typename Type>
+    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.
      * @brief Tries to cast an instance to a given type.
      * @tparam Type Type to which to cast the instance.
      * @tparam Type Type to which to cast the instance.
@@ -1305,6 +1347,80 @@ public:
         return construct(arguments.data(), sizeof...(Args));
         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.<br/>
+     * 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<typename... Args>
+    meta_any invoke(const id_type id, meta_handle instance, Args &&... args) const {
+        std::array<meta_any, sizeof...(Args)> arguments{std::forward<Args>(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.<br/>
+     * 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<typename Type>
+    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<Type>(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.
      * @brief Returns a range to use to visit top-level meta properties.
      * @return An iterable 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<typename... Args>
+meta_any meta_any::invoke(const id_type id, Args &&... args) const {
+    return type().invoke(id, *this, std::forward<Args>(args)...);
+}
+
+
+template<typename Type>
+bool meta_any::set(const id_type id, Type &&value) const {
+    return type().set(id, *this, std::forward<Type>(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 {
 [[nodiscard]] inline meta_type meta_base::parent() const ENTT_NOEXCEPT {
     return node->parent;
     return node->parent;
 }
 }

+ 46 - 0
test/entt/meta/meta_any.cpp

@@ -5,6 +5,17 @@
 #include <entt/meta/meta.hpp>
 #include <entt/meta/meta.hpp>
 #include <entt/meta/resolve.hpp>
 #include <entt/meta/resolve.hpp>
 
 
+
+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 {
 struct empty_t {
     virtual ~empty_t() = default;
     virtual ~empty_t() = default;
     static void destroy(empty_t &) {
     static void destroy(empty_t &) {
@@ -46,6 +57,12 @@ struct MetaAny: ::testing::Test {
         entt::meta<double>().conv<int>();
         entt::meta<double>().conv<int>();
         entt::meta<empty_t>().dtor<&empty_t::destroy>();
         entt::meta<empty_t>().dtor<&empty_t::destroy>();
         entt::meta<fat_t>().base<empty_t>().dtor<&fat_t::destroy>();
         entt::meta<fat_t>().base<empty_t>().dtor<&fat_t::destroy>();
+
+        entt::meta<clazz_t>()
+                .type("clazz"_hs)
+                .data<&clazz_t::value>("value"_hs)
+                .func<&clazz_t::member>("member"_hs)
+                .func<&clazz_t::func>("func"_hs);
     }
     }
 
 
     void SetUp() override {
     void SetUp() override {
@@ -622,3 +639,32 @@ TEST_F(MetaAny, UnmanageableType) {
     ASSERT_TRUE(std::as_const(any).convert<unmanageable_t>());
     ASSERT_TRUE(std::as_const(any).convert<unmanageable_t>());
     ASSERT_FALSE(std::as_const(any).convert<int>());
     ASSERT_FALSE(std::as_const(any).convert<int>());
 }
 }
+
+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<int>());
+	ASSERT_EQ(value_any.cast<int>(), 42);
+
+	ASSERT_FALSE(any.set("non_existent"_hs, 42));
+	ASSERT_FALSE(any.get("non_existent"_hs));
+}

+ 3 - 0
test/entt/meta/meta_type.cpp

@@ -270,6 +270,9 @@ TEST_F(MetaType, Func) {
     ASSERT_TRUE(type.func("func"_hs));
     ASSERT_TRUE(type.func("func"_hs));
     ASSERT_TRUE(type.func("member"_hs).invoke(instance));
     ASSERT_TRUE(type.func("member"_hs).invoke(instance));
     ASSERT_TRUE(type.func("func"_hs).invoke({}));
     ASSERT_TRUE(type.func("func"_hs).invoke({}));
+
+    ASSERT_TRUE(type.invoke("member"_hs, instance));
+    ASSERT_TRUE(type.invoke("func"_hs, {}));
 }
 }
 
 
 TEST_F(MetaType, Construct) {
 TEST_F(MetaType, Construct) {