Browse Source

meta:
* dtor support to member functions
* review to introduce strict checks on policies

Michele Caini 4 years ago
parent
commit
3335b81ad4
4 changed files with 72 additions and 89 deletions
  1. 4 3
      docs/md/meta.md
  2. 37 27
      src/entt/meta/factory.hpp
  3. 29 57
      src/entt/meta/utility.hpp
  4. 2 2
      test/entt/meta/meta_dtor.cpp

+ 4 - 3
docs/md/meta.md

@@ -110,9 +110,10 @@ decorated version of it. This object can be used to add the following:
   entt::meta<my_type>().ctor<int, char>().ctor<&factory>();
   ```
 
-* _Destructors_. Free functions can be set as destructors of reflected types.
-  The purpose is to give users the ability to free up resources that require
-  special treatment before an object is actually destroyed.<br/>
+* _Destructors_. Free functions and member functions can be used as destructors
+  of reflected types. The purpose is to give users the ability to free up
+  resources that require special treatment before an object is actually
+  destroyed.<br/>
   Use the `dtor` member function for this purpose:
 
   ```cpp

+ 37 - 27
src/entt/meta/factory.hpp

@@ -3,6 +3,7 @@
 
 #include <algorithm>
 #include <cstddef>
+#include <functional>
 #include <tuple>
 #include <type_traits>
 #include <utility>
@@ -192,7 +193,7 @@ class meta_factory<Type> {
             (std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none,
             internal::meta_node<std::remove_const_t<std::remove_reference_t<data_type>>>::resolve(),
             &meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>,
-            [](meta_handle instance, meta_any value) -> bool { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value) || ...); },
+            [](meta_handle instance, meta_any value) -> bool { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); },
             &meta_getter<Type, Getter, Policy>
             // tricks clang-format
         };
@@ -269,26 +270,16 @@ public:
     auto conv() ENTT_NOEXCEPT {
         using conv_type = std::remove_const_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
 
-        if constexpr(std::is_member_function_pointer_v<decltype(Candidate)>) {
-            static internal::meta_conv_node node{
-                nullptr,
-                internal::meta_node<conv_type>::resolve(),
-                [](const meta_any &instance) -> meta_any { return forward_as_meta((static_cast<const Type *>(instance.data())->*Candidate)()); }
-                // tricks clang-format
-            };
-
-            link_conv_if_required(node);
-        } else {
-            static internal::meta_conv_node node{
-                nullptr,
-                internal::meta_node<conv_type>::resolve(),
-                [](const meta_any &instance) -> meta_any { return forward_as_meta(Candidate(*static_cast<const Type *>(instance.data()))); }
-                // tricks clang-format
-            };
-
-            link_conv_if_required(node);
-        }
+        static internal::meta_conv_node node{
+            nullptr,
+            internal::meta_node<conv_type>::resolve(),
+            [](const meta_any &instance) -> meta_any {
+                return forward_as_meta(std::invoke(Candidate, *static_cast<const Type *>(instance.data())));
+            }
+            // tricks clang-format
+        };
 
+        link_conv_if_required(node);
         return meta_factory<Type>{};
     }
 
@@ -375,13 +366,15 @@ public:
     /**
      * @brief Assigns a meta destructor to a meta type.
      *
-     * Free functions can be assigned to meta types in the role of destructors.
-     * The signature of the function should identical to the following:
+     * Both free functions and member functions can be assigned to meta types in
+     * the role of destructors.<br/>
+     * The signature of a free function should be identical to the following:
      *
      * @code{.cpp}
      * void(Type &);
      * @endcode
      *
+     * Member functions should not take arguments instead.<br/>
      * The purpose is to give users the ability to free up resources that
      * require special treatment before an object is actually destroyed.
      *
@@ -391,7 +384,7 @@ public:
     template<auto Func>
     auto dtor() ENTT_NOEXCEPT {
         static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
-        owner->dtor = [](void *instance) { Func(*static_cast<Type *>(instance)); };
+        owner->dtor = [](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
         return meta_factory<Type>{};
     }
 
@@ -411,7 +404,24 @@ public:
     template<auto Data, typename Policy = as_is_t>
     auto data(const id_type id) ENTT_NOEXCEPT {
         if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
-            return data<Data, Data, Policy>(id);
+            using data_type = std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>;
+
+            static internal::meta_data_node node{
+                {},
+                nullptr,
+                nullptr,
+                1u,
+                /* this is never static */
+                std::is_const_v<data_type> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
+                internal::meta_node<std::remove_const_t<data_type>>::resolve(),
+                &meta_arg<type_list<std::remove_const_t<data_type>>>,
+                &meta_setter<Type, Data>,
+                &meta_getter<Type, Data, Policy>
+                // tricks clang-format
+            };
+
+            link_data_if_required(id, node);
+            return meta_factory<Type, std::integral_constant<decltype(Data), Data>, std::integral_constant<decltype(Data), Data>>{&node.prop};
         } else {
             using data_type = std::remove_pointer_t<decltype(Data)>;
 
@@ -422,7 +432,7 @@ public:
                 1u,
                 ((std::is_same_v<Type, data_type> || std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
                 internal::meta_node<std::remove_const_t<std::remove_reference_t<data_type>>>::resolve(),
-                &meta_arg<type_list<data_type>>,
+                &meta_arg<type_list<std::remove_const_t<std::remove_reference_t<data_type>>>>,
                 &meta_setter<Type, Data>,
                 &meta_getter<Type, Data, Policy>
                 // tricks clang-format
@@ -482,8 +492,8 @@ public:
                 nullptr,
                 nullptr,
                 1u,
-                /* this is never static */
-                (std::is_member_object_pointer_v<decltype(Setter)> && std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none,
+                /* this is never static nor const */
+                internal::meta_traits::is_none,
                 internal::meta_node<std::remove_const_t<std::remove_reference_t<data_type>>>::resolve(),
                 &meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>,
                 &meta_setter<Type, Setter>,

+ 29 - 57
src/entt/meta/utility.hpp

@@ -2,6 +2,7 @@
 #define ENTT_META_UTILITY_HPP
 
 #include <cstddef>
+#include <functional>
 #include <type_traits>
 #include <utility>
 #include "../config/config.h"
@@ -65,14 +66,14 @@ struct meta_function_descriptor<Type, Ret (Class::*)(Args...)> {
 template<typename Type, typename Ret, typename Class>
 struct meta_function_descriptor<Type, Ret Class::*> {
     /*! @brief Meta data return type. */
-    using return_type = Ret;
+    using return_type = Ret &;
     /*! @brief Meta data arguments. */
-    using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Ret>, type_list<Class &, Ret>>;
+    using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<>, type_list<Class &>>;
 
     /*! @brief True if the meta data is const, false otherwise. */
     static constexpr auto is_const = false;
     /*! @brief True if the meta data is static, false otherwise. */
-    static constexpr auto is_static = false;
+    static constexpr auto is_static = !std::is_base_of_v<Class, Type>;
 };
 
 /**
@@ -195,27 +196,20 @@ template<typename Type>
 template<typename Type, auto Data>
 [[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) {
     if constexpr(!std::is_same_v<decltype(Data), Type> && !std::is_same_v<decltype(Data), std::nullptr_t>) {
-        if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
+        if constexpr(std::is_member_function_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
             using descriptor = meta_function_helper_t<Type, decltype(Data)>;
             using data_type = type_list_element_t<descriptor::is_static, typename descriptor::args_type>;
 
             if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
-                Data(*clazz, value.cast<data_type>());
-                return true;
-            }
-        } else if constexpr(std::is_member_function_pointer_v<decltype(Data)>) {
-            using data_type = type_list_element_t<0u, typename meta_function_helper_t<Type, decltype(Data)>::args_type>;
-
-            if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
-                (clazz->*Data)(value.cast<data_type>());
+                std::invoke(Data, *clazz, value.cast<data_type>());
                 return true;
             }
         } else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
-            using data_type = std::remove_reference_t<decltype(std::declval<Type>().*Data)>;
+            using data_type = std::remove_reference_t<typename meta_function_helper_t<Type, decltype(Data)>::return_type>;
 
             if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) {
                 if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
-                    clazz->*Data = value.cast<data_type>();
+                    std::invoke(Data, *clazz) = value.cast<data_type>();
                     return true;
                 }
             }
@@ -244,18 +238,18 @@ template<typename Type, auto Data>
  */
 template<typename Type, auto Data, typename Policy = as_is_t>
 [[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) {
-    if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
-        auto *const clazz = instance->try_cast<std::conditional_t<std::is_invocable_v<decltype(Data), const Type &>, const Type, Type>>();
-        return clazz ? meta_dispatch<Policy>(Data(*clazz)) : meta_any{};
-    } else if constexpr(std::is_member_function_pointer_v<decltype(Data)>) {
-        auto *const clazz = instance->try_cast<std::conditional_t<std::is_invocable_v<decltype(Data), const Type &>, const Type, Type>>();
-        return clazz ? meta_dispatch<Policy>((clazz->*Data)()) : meta_any{};
-    } else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
-        if constexpr(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<decltype(std::declval<Type>().*Data)>>>) {
-            if(auto *clazz = instance->try_cast<Type>(); clazz) {
-                return meta_dispatch<Policy>(clazz->*Data);
-            } else if(auto *fallback = instance->try_cast<const Type>(); fallback) {
-                return meta_dispatch<Policy>(fallback->*Data);
+    if constexpr(std::is_member_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
+        if constexpr(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>>>) {
+            if constexpr(std::is_invocable_v<decltype(Data), Type &>) {
+                if(auto *clazz = instance->try_cast<Type>(); clazz) {
+                    return meta_dispatch<Policy>(std::invoke(Data, *clazz));
+                }
+            }
+
+            if constexpr(std::is_invocable_v<decltype(Data), const Type &>) {
+                if(auto *fallback = instance->try_cast<const Type>(); fallback) {
+                    return meta_dispatch<Policy>(std::invoke(Data, *fallback));
+                }
             }
         }
 
@@ -278,22 +272,13 @@ template<typename Type, auto Data, typename Policy = as_is_t>
 
 namespace internal {
 
-template<typename Type, typename Policy, typename Candidate, typename First, typename... Other>
-[[nodiscard]] meta_any meta_invoke_with_args(Candidate &&candidate, First &&maybe_clazz, Other &&...other) {
-    using descriptor = meta_function_helper_t<Type, std::remove_reference_t<Candidate>>;
-
-    if constexpr(std::is_member_function_pointer_v<std::remove_reference_t<Candidate>>) {
-        if constexpr(std::is_void_v<typename descriptor::return_type>) {
-            (std::forward<First>(maybe_clazz).*std::forward<Candidate>(candidate))(std::forward<Other>(other)...);
-            return meta_any{std::in_place_type<void>};
-        } else {
-            return meta_dispatch<Policy>((std::forward<First>(maybe_clazz).*std::forward<Candidate>(candidate))(std::forward<Other>(other)...));
-        }
-    } else if constexpr(std::is_void_v<typename descriptor::return_type>) {
-        std::forward<Candidate>(candidate)(std::forward<First>(maybe_clazz), std::forward<Other>(other)...);
+template<typename Type, typename Policy, typename Candidate, typename... Args>
+[[nodiscard]] meta_any meta_invoke_with_args(Candidate &&candidate, Args &&...args) {
+    if constexpr(std::is_same_v<std::invoke_result_t<decltype(candidate), Args...>, void>) {
+        std::invoke(candidate, args...);
         return meta_any{std::in_place_type<void>};
     } else {
-        return meta_dispatch<Policy>(std::forward<Candidate>(candidate)(std::forward<First>(maybe_clazz), std::forward<Other>(other)...));
+        return meta_dispatch<Policy>(std::invoke(candidate, args...));
     }
 }
 
@@ -305,26 +290,17 @@ template<typename Type, typename Policy, typename Candidate, std::size_t... Inde
         if(const auto *const clazz = instance->try_cast<const Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
             return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
         }
-
-        return meta_any{};
     } else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
         if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
             return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
         }
-
-        return meta_any{};
-    } else if constexpr(sizeof...(Index) != 0u) {
+    } else {
         if(((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
             return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
         }
-
-        return meta_any{};
-    } else if constexpr(std::is_void_v<decltype(std::forward<Candidate>(candidate)())>) {
-        std::forward<Candidate>(candidate)();
-        return meta_any{std::in_place_type<void>};
-    } else {
-        return meta_dispatch<Policy>(std::forward<Candidate>(candidate)());
     }
+
+    return meta_any{};
 }
 
 template<typename Type, typename... Args, std::size_t... Index>
@@ -412,11 +388,7 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
  */
 template<typename Type, auto Candidate, typename Policy = as_is_t>
 [[nodiscard]] meta_any meta_construct(meta_any *const args) {
-    if constexpr(meta_function_helper_t<Type, decltype(Candidate)>::is_static) {
-        return internal::meta_invoke<Type, Policy>({}, Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
-    } else {
-        return internal::meta_invoke<Type, Policy>(*args, Candidate, args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
-    }
+    return meta_construct<Type, Policy>(Candidate, args);
 }
 
 } // namespace entt

+ 2 - 2
test/entt/meta/meta_dtor.cpp

@@ -14,7 +14,7 @@ struct clazz_t {
         --counter;
     }
 
-    static void destroy_incr(clazz_t &) {
+    void destroy_incr() const {
         ++counter;
     }
 
@@ -104,7 +104,7 @@ TEST_F(MetaDtor, ReRegistration) {
 
     ASSERT_NE(node->dtor, nullptr);
 
-    entt::meta<clazz_t>().dtor<clazz_t::destroy_incr>();
+    entt::meta<clazz_t>().dtor<&clazz_t::destroy_incr>();
     entt::resolve<clazz_t>().construct().reset();
 
     ASSERT_EQ(clazz_t::counter, 2);