Przeglądaj źródła

meta: extended template info support

Michele Caini 5 lat temu
rodzic
commit
bcaf1489c0

+ 0 - 3
TODO

@@ -4,8 +4,6 @@
 * work stealing job system (see #100) + mt scheduler based on const awareness for types
 * work stealing job system (see #100) + mt scheduler based on const awareness for types
 * allow to replace std:: with custom implementations
 * allow to replace std:: with custom implementations
 * add examples (and credits) from @alanjfs :)
 * add examples (and credits) from @alanjfs :)
-* static reflection, hint: template<> meta_type_t<Type>: meta_descriptor<name, func..., props..., etc...> (see #342)
-* update documentation for meta, it contains less than half of the actual feature
 * custom pools example (multi instance, tables, enable/disable, and so on...)
 * custom pools example (multi instance, tables, enable/disable, and so on...)
 
 
 WIP:
 WIP:
@@ -28,4 +26,3 @@ WIP:
 * snapshot: support for range-based archives
 * snapshot: support for range-based archives
 * page size 0 -> page less mode
 * page size 0 -> page less mode
 * add example: 64 bit ids with 32 bits reserved for users' purposes
 * add example: 64 bit ids with 32 bits reserved for users' purposes
-* add meta dynamic cast (search base for T in parent, we have the meta type already)

+ 57 - 0
docs/md/meta.md

@@ -12,6 +12,7 @@
   * [Enjoy the runtime](#enjoy-the-runtime)
   * [Enjoy the runtime](#enjoy-the-runtime)
   * [Container support](#container-support)
   * [Container support](#container-support)
   * [Pointer-like types](#pointer-like-types)
   * [Pointer-like types](#pointer-like-types)
+  * [Template information](#template-information)
   * [Implicitly generated default constructor](#implicitly-generated-default-constructor)
   * [Implicitly generated default constructor](#implicitly-generated-default-constructor)
   * [Policies: the more, the less](#policies-the-more-the-less)
   * [Policies: the more, the less](#policies-the-more-the-less)
   * [Named constants and enums](#named-constants-and-enums)
   * [Named constants and enums](#named-constants-and-enums)
@@ -657,6 +658,62 @@ In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However,
 In all other cases, that is, when dereferencing a pointer works as expected and
 In all other cases, that is, when dereferencing a pointer works as expected and
 regardless of the pointed type, no user intervention is required.
 regardless of the pointed type, no user intervention is required.
 
 
+## Template information
+
+Meta types also provide a minimal set of information about the nature of the
+original type in case it's a class template.<br/>
+By default, this works out of the box and requires no user action. However, it's
+important to include the header file `template.hpp` to make these information
+available to the compiler when needed.
+
+Meta template information are easily found:
+
+```cpp
+// this method returns true if the type is recognized as a class template specialization
+if(auto type = entt::resolve<std::shared_ptr<my_type>>(); type.is_template_specialization()) {
+    // meta type of the class template conveniently wrapped by entt::meta_class_template_tag
+    auto class_type = type.template_type();
+
+    // number of template arguments
+    std::size_t arity = type.template_arity();
+
+    // meta type of the i-th argument
+    auto arg_type = type.template_arg(0u);
+}
+```
+
+Typically, when template information for a type are required, what the library
+provides is sufficient. However, there are some cases where a user may want more
+details or a different set of information.<br/>
+Consider the case of a class template that is meant to wrap function types:
+
+```cpp
+template<typename>
+struct function_type;
+
+template<typename Ret, typename... Args>
+struct function_type<Ret(Args...)> {};
+```
+
+In this case, rather than the function type, the user might want the return type
+and unpacked arguments as if they were different template parameters for the
+original class template.<br/>
+To achieve this, users must enter the library internals and provide their own
+specialization for the class template `entt::meta_template_traits`, such as:
+
+```cpp
+template<typename Ret, typename... Args>
+struct entt::meta_template_traits<function_type<Ret(Args...)>> {
+    using class_type = meta_class_template_tag<function_type>;
+    using args_type = type_list<Ret, Args...>;
+};
+```
+
+The reflection system doesn't verify the accuracy of the information nor infer a
+correspondence between real types and meta types.<br/>
+Therefore, the specialization will be used as is and the information it contains
+will be associated with the appropriate type when required.
+
 ## Implicitly generated default constructor
 ## Implicitly generated default constructor
 
 
 In many cases, it's useful to be able to create objects of default constructible
 In many cases, it's useful to be able to create objects of default constructible

+ 1 - 0
src/entt/entt.hpp

@@ -35,6 +35,7 @@
 #include "meta/policy.hpp"
 #include "meta/policy.hpp"
 #include "meta/range.hpp"
 #include "meta/range.hpp"
 #include "meta/resolve.hpp"
 #include "meta/resolve.hpp"
+#include "meta/template.hpp"
 #include "meta/type_traits.hpp"
 #include "meta/type_traits.hpp"
 #include "meta/utility.hpp"
 #include "meta/utility.hpp"
 #include "platform/android-ndk-r17.hpp"
 #include "platform/android-ndk-r17.hpp"

+ 24 - 20
src/entt/meta/node.hpp

@@ -17,11 +17,6 @@
 namespace entt {
 namespace entt {
 
 
 
 
-/*! @brief Utility class to disambiguate class templates. */
-template<template<typename...> typename>
-struct meta_class_template_tag {};
-
-
 class meta_any;
 class meta_any;
 class meta_type;
 class meta_type;
 struct meta_handle;
 struct meta_handle;
@@ -167,6 +162,10 @@ auto meta_visit(const Op &op, const Node *node)
 }
 }
 
 
 
 
+template<typename... Args>
+meta_type_node * meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT;
+
+
 template<typename Type>
 template<typename Type>
 class ENTT_API meta_node {
 class ENTT_API meta_node {
     static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Invalid type");
     static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Invalid type");
@@ -195,20 +194,19 @@ class ENTT_API meta_node {
         }
         }
     }
     }
 
 
-    template<template<typename...> typename Clazz, typename... Args>
-    [[nodiscard]] static meta_template_info template_info(type_identity<Clazz<Args...>>) ENTT_NOEXCEPT {
-        return {
-            true,
-            sizeof...(Args),
-            &meta_node<meta_class_template_tag<Clazz>>::resolve,
-            [](const std::size_t index) ENTT_NOEXCEPT {
-                return std::array<meta_type_node *, sizeof...(Args)>{{internal::meta_node<std::remove_cv_t<std::remove_reference_t<Args>>>::resolve()...}}[index];
-            }
-        };
-    }
-
-    [[nodiscard]] static meta_template_info template_info(...) ENTT_NOEXCEPT {
-        return { false, 0u, nullptr, nullptr };
+    [[nodiscard]] static meta_template_info meta_template_descriptor() ENTT_NOEXCEPT {
+        if constexpr(is_complete_v<meta_template_traits<Type>>) {
+            return {
+                true,
+                meta_template_traits<Type>::args_type::size,
+                &meta_node<typename meta_template_traits<Type>::class_type>::resolve,
+                [](const std::size_t index) ENTT_NOEXCEPT {
+                    return meta_arg_node(typename meta_template_traits<Type>::args_type{}, index);
+                }
+            };
+        } else {
+            return { false, 0u, nullptr, nullptr };
+        }
     }
     }
 
 
 public:
 public:
@@ -233,7 +231,7 @@ public:
             is_meta_pointer_like_v<Type>,
             is_meta_pointer_like_v<Type>,
             is_complete_v<meta_sequence_container_traits<Type>>,
             is_complete_v<meta_sequence_container_traits<Type>>,
             is_complete_v<meta_associative_container_traits<Type>>,
             is_complete_v<meta_associative_container_traits<Type>>,
-            template_info(type_identity<Type>{}),
+            meta_template_descriptor(),
             std::rank_v<Type>,
             std::rank_v<Type>,
             [](meta_type_node::size_type dim) ENTT_NOEXCEPT { return extent(dim, std::make_index_sequence<std::rank_v<Type>>{}); },
             [](meta_type_node::size_type dim) ENTT_NOEXCEPT { return extent(dim, std::make_index_sequence<std::rank_v<Type>>{}); },
             &meta_node<std::remove_cv_t<std::remove_pointer_t<Type>>>::resolve,
             &meta_node<std::remove_cv_t<std::remove_pointer_t<Type>>>::resolve,
@@ -251,6 +249,12 @@ template<typename Type>
 struct meta_info: meta_node<std::remove_cv_t<std::remove_reference_t<Type>>> {};
 struct meta_info: meta_node<std::remove_cv_t<std::remove_reference_t<Type>>> {};
 
 
 
 
+template<typename... Args>
+meta_type_node * meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT {
+    return std::array<meta_type_node *, sizeof...(Args)>{{internal::meta_info<Args>::resolve()...}}[index];
+}
+
+
 }
 }
 
 
 
 

+ 33 - 0
src/entt/meta/template.hpp

@@ -0,0 +1,33 @@
+#ifndef ENTT_META_TEMPLATE_HPP
+#define ENTT_META_TEMPLATE_HPP
+
+
+#include "../core/type_traits.hpp"
+
+
+namespace entt {
+
+
+/*! @brief Utility class to disambiguate class templates. */
+template<template<typename...> typename>
+struct meta_class_template_tag {};
+
+
+/**
+ * @brief General purpose traits class for generating meta template information.
+ * @tparam Clazz Type of class template.
+ * @tparam Args Types of template arguments.
+ */
+template<template<typename...> typename Clazz, typename... Args>
+struct meta_template_traits<Clazz<Args...>> {
+    /*! @brief Wrapped class template. */
+    using class_type = meta_class_template_tag<Clazz>;
+    /*! @brief List of template arguments. */
+    using args_type = type_list<Args...>;
+};
+
+
+}
+
+
+#endif

+ 8 - 0
src/entt/meta/type_traits.hpp

@@ -8,6 +8,14 @@
 namespace entt {
 namespace entt {
 
 
 
 
+/**
+ * @brief Traits class template to be specialized to enable support for meta
+ * template information.
+ */
+template<typename>
+struct meta_template_traits;
+
+
 /**
 /**
  * @brief Traits class template to be specialized to enable support for meta
  * @brief Traits class template to be specialized to enable support for meta
  * sequence containers.
  * sequence containers.

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

@@ -126,7 +126,7 @@ using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::t
  */
  */
 template<typename... Args>
 template<typename... Args>
 [[nodiscard]] static meta_type meta_arg(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT {
 [[nodiscard]] static meta_type meta_arg(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT {
-    return std::array<meta_type, sizeof...(Args)>{{internal::meta_info<Args>::resolve()...}}[index];
+    return internal::meta_arg_node(type_list<Args...>{}, index);
 }
 }
 
 
 
 

+ 1 - 0
test/CMakeLists.txt

@@ -199,6 +199,7 @@ SETUP_BASIC_TEST(meta_handle entt/meta/meta_handle.cpp)
 SETUP_BASIC_TEST(meta_pointer entt/meta/meta_pointer.cpp)
 SETUP_BASIC_TEST(meta_pointer entt/meta/meta_pointer.cpp)
 SETUP_BASIC_TEST(meta_prop entt/meta/meta_prop.cpp)
 SETUP_BASIC_TEST(meta_prop entt/meta/meta_prop.cpp)
 SETUP_BASIC_TEST(meta_range entt/meta/meta_range.cpp)
 SETUP_BASIC_TEST(meta_range entt/meta/meta_range.cpp)
+SETUP_BASIC_TEST(meta_template entt/meta/meta_template.cpp)
 SETUP_BASIC_TEST(meta_type entt/meta/meta_type.cpp)
 SETUP_BASIC_TEST(meta_type entt/meta/meta_type.cpp)
 
 
 # Test poly
 # Test poly

+ 49 - 0
test/entt/meta/meta_template.cpp

@@ -0,0 +1,49 @@
+#include <gtest/gtest.h>
+#include <entt/core/type_traits.hpp>
+#include <entt/meta/meta.hpp>
+#include <entt/meta/resolve.hpp>
+#include <entt/meta/template.hpp>
+
+template<typename>
+struct function_type;
+
+template<typename Ret, typename... Args>
+struct function_type<Ret(Args...)> {};
+
+template<typename Ret, typename... Args>
+struct entt::meta_template_traits<function_type<Ret(Args...)>> {
+    using class_type = meta_class_template_tag<function_type>;
+    using args_type = type_list<Ret, Args...>;
+};
+
+TEST(MetaTemplate, Invalid) {
+    const auto type = entt::resolve<int>();
+
+    ASSERT_FALSE(type.is_template_specialization());
+    ASSERT_EQ(type.template_arity(), 0u);
+    ASSERT_EQ(type.template_type(), entt::meta_type{});
+    ASSERT_EQ(type.template_arg(0u), entt::meta_type{});
+}
+
+TEST(MetaTemplate, Valid) {
+    const auto type = entt::resolve<entt::type_list<int, char>>();
+
+    ASSERT_TRUE(type.is_template_specialization());
+    ASSERT_EQ(type.template_arity(), 2u);
+    ASSERT_EQ(type.template_type(), entt::resolve<entt::meta_class_template_tag<entt::type_list>>());
+    ASSERT_EQ(type.template_arg(0u), entt::resolve<int>());
+    ASSERT_EQ(type.template_arg(1u), entt::resolve<char>());
+    ASSERT_EQ(type.template_arg(2u), entt::meta_type{});
+}
+
+TEST(MetaTemplate, CustomTraits) {
+    const auto type = entt::resolve<function_type<void(int, char)>>();
+
+    ASSERT_TRUE(type.is_template_specialization());
+    ASSERT_EQ(type.template_arity(), 3u);
+    ASSERT_EQ(type.template_type(), entt::resolve<entt::meta_class_template_tag<function_type>>());
+    ASSERT_EQ(type.template_arg(0u), entt::resolve<void>());
+    ASSERT_EQ(type.template_arg(1u), entt::resolve<int>());
+    ASSERT_EQ(type.template_arg(2u), entt::resolve<char>());
+    ASSERT_EQ(type.template_arg(3u), entt::meta_type{});
+}

+ 14 - 13
test/entt/meta/meta_type.cpp

@@ -8,6 +8,7 @@
 #include <entt/meta/meta.hpp>
 #include <entt/meta/meta.hpp>
 #include <entt/meta/pointer.hpp>
 #include <entt/meta/pointer.hpp>
 #include <entt/meta/resolve.hpp>
 #include <entt/meta/resolve.hpp>
+#include <entt/meta/template.hpp>
 
 
 template<typename Type>
 template<typename Type>
 void set(Type &prop, Type value) {
 void set(Type &prop, Type value) {
@@ -269,6 +270,19 @@ TEST_F(MetaType, Traits) {
     ASSERT_EQ(entt::resolve<int[5][3]>().extent(2u), 0u);
     ASSERT_EQ(entt::resolve<int[5][3]>().extent(2u), 0u);
 }
 }
 
 
+TEST_F(MetaType, TemplateInfo) {
+    ASSERT_FALSE(entt::resolve<int>().is_template_specialization());
+    ASSERT_EQ(entt::resolve<int>().template_arity(), 0u);
+    ASSERT_EQ(entt::resolve<int>().template_type(), entt::meta_type{});
+    ASSERT_EQ(entt::resolve<int>().template_arg(0u), entt::meta_type{});
+
+    ASSERT_TRUE(entt::resolve<std::shared_ptr<int>>().is_template_specialization());
+    ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_arity(), 1u);
+    ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_type(), entt::resolve<entt::meta_class_template_tag<std::shared_ptr>>());
+    ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_arg(0u), entt::resolve<int>());
+    ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_arg(1u), entt::meta_type{});
+}
+
 TEST_F(MetaType, RemovePointer) {
 TEST_F(MetaType, RemovePointer) {
     ASSERT_EQ(entt::resolve<void *>().remove_pointer(), entt::resolve<void>());
     ASSERT_EQ(entt::resolve<void *>().remove_pointer(), entt::resolve<void>());
     ASSERT_EQ(entt::resolve<int(*)(char, double)>().remove_pointer(), entt::resolve<int(char, double)>());
     ASSERT_EQ(entt::resolve<int(*)(char, double)>().remove_pointer(), entt::resolve<int(char, double)>());
@@ -654,19 +668,6 @@ TEST_F(MetaType, ResetAndReRegistrationAfterReset) {
     ASSERT_TRUE(entt::resolve<property_t>().data("rand"_hs).prop(property_t::random));
     ASSERT_TRUE(entt::resolve<property_t>().data("rand"_hs).prop(property_t::random));
 }
 }
 
 
-TEST_F(MetaType, ClassTemplate) {
-    ASSERT_FALSE(entt::resolve<int>().is_template_specialization());
-    ASSERT_EQ(entt::resolve<int>().template_arity(), 0u);
-    ASSERT_EQ(entt::resolve<int>().template_type(), entt::meta_type{});
-    ASSERT_EQ(entt::resolve<int>().template_arg(0u), entt::meta_type{});
-
-    ASSERT_TRUE(entt::resolve<std::shared_ptr<int>>().is_template_specialization());
-    ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_arity(), 1u);
-    ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_type(), entt::resolve<entt::meta_class_template_tag<std::shared_ptr>>());
-    ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_arg(0u), entt::resolve<int>());
-    ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_arg(1u), entt::meta_type{});
-}
-
 TEST_F(MetaType, ReRegistration) {
 TEST_F(MetaType, ReRegistration) {
     using namespace entt::literals;
     using namespace entt::literals;