Explorar el Código

meta: extended template info support

Michele Caini hace 5 años
padre
commit
bcaf1489c0

+ 0 - 3
TODO

@@ -4,8 +4,6 @@
 * work stealing job system (see #100) + mt scheduler based on const awareness for types
 * allow to replace std:: with custom implementations
 * 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...)
 
 WIP:
@@ -28,4 +26,3 @@ WIP:
 * snapshot: support for range-based archives
 * page size 0 -> page less mode
 * 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)
   * [Container support](#container-support)
   * [Pointer-like types](#pointer-like-types)
+  * [Template information](#template-information)
   * [Implicitly generated default constructor](#implicitly-generated-default-constructor)
   * [Policies: the more, the less](#policies-the-more-the-less)
   * [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
 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
 
 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/range.hpp"
 #include "meta/resolve.hpp"
+#include "meta/template.hpp"
 #include "meta/type_traits.hpp"
 #include "meta/utility.hpp"
 #include "platform/android-ndk-r17.hpp"

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

@@ -17,11 +17,6 @@
 namespace entt {
 
 
-/*! @brief Utility class to disambiguate class templates. */
-template<template<typename...> typename>
-struct meta_class_template_tag {};
-
-
 class meta_any;
 class meta_type;
 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>
 class ENTT_API meta_node {
     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:
@@ -233,7 +231,7 @@ public:
             is_meta_pointer_like_v<Type>,
             is_complete_v<meta_sequence_container_traits<Type>>,
             is_complete_v<meta_associative_container_traits<Type>>,
-            template_info(type_identity<Type>{}),
+            meta_template_descriptor(),
             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,
@@ -251,6 +249,12 @@ template<typename 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 {
 
 
+/**
+ * @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
  * 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>
 [[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_prop entt/meta/meta_prop.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)
 
 # 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/pointer.hpp>
 #include <entt/meta/resolve.hpp>
+#include <entt/meta/template.hpp>
 
 template<typename Type>
 void set(Type &prop, Type value) {
@@ -269,6 +270,19 @@ TEST_F(MetaType, Traits) {
     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) {
     ASSERT_EQ(entt::resolve<void *>().remove_pointer(), entt::resolve<void>());
     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));
 }
 
-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) {
     using namespace entt::literals;