diff --git a/TODO b/TODO index c9f147877..c6639d79c 100644 --- a/TODO +++ b/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: meta_descriptor (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) diff --git a/docs/md/meta.md b/docs/md/meta.md index 8b401adcc..f35860f3f 100644 --- a/docs/md/meta.md +++ b/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.
+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>(); 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.
+Consider the case of a class template that is meant to wrap function types: + +```cpp +template +struct function_type; + +template +struct function_type {}; +``` + +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.
+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 +struct entt::meta_template_traits> { + using class_type = meta_class_template_tag; + using args_type = type_list; +}; +``` + +The reflection system doesn't verify the accuracy of the information nor infer a +correspondence between real types and meta types.
+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 diff --git a/src/entt/entt.hpp b/src/entt/entt.hpp index 8ebf96902..c308401e1 100644 --- a/src/entt/entt.hpp +++ b/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" diff --git a/src/entt/meta/node.hpp b/src/entt/meta/node.hpp index 6a6712622..a341d9bcd 100644 --- a/src/entt/meta/node.hpp +++ b/src/entt/meta/node.hpp @@ -17,11 +17,6 @@ namespace entt { -/*! @brief Utility class to disambiguate class templates. */ -template 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 +meta_type_node * meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT; + + template class ENTT_API meta_node { static_assert(std::is_same_v>>, "Invalid type"); @@ -195,20 +194,19 @@ class ENTT_API meta_node { } } - template typename Clazz, typename... Args> - [[nodiscard]] static meta_template_info template_info(type_identity>) ENTT_NOEXCEPT { - return { - true, - sizeof...(Args), - &meta_node>::resolve, - [](const std::size_t index) ENTT_NOEXCEPT { - return std::array{{internal::meta_node>>::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>) { + return { + true, + meta_template_traits::args_type::size, + &meta_node::class_type>::resolve, + [](const std::size_t index) ENTT_NOEXCEPT { + return meta_arg_node(typename meta_template_traits::args_type{}, index); + } + }; + } else { + return { false, 0u, nullptr, nullptr }; + } } public: @@ -233,7 +231,7 @@ public: is_meta_pointer_like_v, is_complete_v>, is_complete_v>, - template_info(type_identity{}), + meta_template_descriptor(), std::rank_v, [](meta_type_node::size_type dim) ENTT_NOEXCEPT { return extent(dim, std::make_index_sequence>{}); }, &meta_node>>::resolve, @@ -251,6 +249,12 @@ template struct meta_info: meta_node>> {}; +template +meta_type_node * meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT { + return std::array{{internal::meta_info::resolve()...}}[index]; +} + + } diff --git a/src/entt/meta/template.hpp b/src/entt/meta/template.hpp new file mode 100644 index 000000000..11f1d25bc --- /dev/null +++ b/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 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 typename Clazz, typename... Args> +struct meta_template_traits> { + /*! @brief Wrapped class template. */ + using class_type = meta_class_template_tag; + /*! @brief List of template arguments. */ + using args_type = type_list; +}; + + +} + + +#endif diff --git a/src/entt/meta/type_traits.hpp b/src/entt/meta/type_traits.hpp index 89c4d942c..8fe91aa55 100644 --- a/src/entt/meta/type_traits.hpp +++ b/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 +struct meta_template_traits; + + /** * @brief Traits class template to be specialized to enable support for meta * sequence containers. diff --git a/src/entt/meta/utility.hpp b/src/entt/meta/utility.hpp index c86f39b54..9d429909a 100644 --- a/src/entt/meta/utility.hpp +++ b/src/entt/meta/utility.hpp @@ -126,7 +126,7 @@ using meta_function_helper_t = typename meta_function_helper::t */ template [[nodiscard]] static meta_type meta_arg(type_list, const std::size_t index) ENTT_NOEXCEPT { - return std::array{{internal::meta_info::resolve()...}}[index]; + return internal::meta_arg_node(type_list{}, index); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dda4084d4..fb8330834 100644 --- a/test/CMakeLists.txt +++ b/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 diff --git a/test/entt/meta/meta_template.cpp b/test/entt/meta/meta_template.cpp new file mode 100644 index 000000000..10dfba975 --- /dev/null +++ b/test/entt/meta/meta_template.cpp @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include + +template +struct function_type; + +template +struct function_type {}; + +template +struct entt::meta_template_traits> { + using class_type = meta_class_template_tag; + using args_type = type_list; +}; + +TEST(MetaTemplate, Invalid) { + const auto type = entt::resolve(); + + 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>(); + + ASSERT_TRUE(type.is_template_specialization()); + ASSERT_EQ(type.template_arity(), 2u); + ASSERT_EQ(type.template_type(), entt::resolve>()); + ASSERT_EQ(type.template_arg(0u), entt::resolve()); + ASSERT_EQ(type.template_arg(1u), entt::resolve()); + ASSERT_EQ(type.template_arg(2u), entt::meta_type{}); +} + +TEST(MetaTemplate, CustomTraits) { + const auto type = entt::resolve>(); + + ASSERT_TRUE(type.is_template_specialization()); + ASSERT_EQ(type.template_arity(), 3u); + ASSERT_EQ(type.template_type(), entt::resolve>()); + ASSERT_EQ(type.template_arg(0u), entt::resolve()); + ASSERT_EQ(type.template_arg(1u), entt::resolve()); + ASSERT_EQ(type.template_arg(2u), entt::resolve()); + ASSERT_EQ(type.template_arg(3u), entt::meta_type{}); +} diff --git a/test/entt/meta/meta_type.cpp b/test/entt/meta/meta_type.cpp index 4019836ea..a8992ccd0 100644 --- a/test/entt/meta/meta_type.cpp +++ b/test/entt/meta/meta_type.cpp @@ -8,6 +8,7 @@ #include #include #include +#include template void set(Type &prop, Type value) { @@ -269,6 +270,19 @@ TEST_F(MetaType, Traits) { ASSERT_EQ(entt::resolve().extent(2u), 0u); } +TEST_F(MetaType, TemplateInfo) { + ASSERT_FALSE(entt::resolve().is_template_specialization()); + ASSERT_EQ(entt::resolve().template_arity(), 0u); + ASSERT_EQ(entt::resolve().template_type(), entt::meta_type{}); + ASSERT_EQ(entt::resolve().template_arg(0u), entt::meta_type{}); + + ASSERT_TRUE(entt::resolve>().is_template_specialization()); + ASSERT_EQ(entt::resolve>().template_arity(), 1u); + ASSERT_EQ(entt::resolve>().template_type(), entt::resolve>()); + ASSERT_EQ(entt::resolve>().template_arg(0u), entt::resolve()); + ASSERT_EQ(entt::resolve>().template_arg(1u), entt::meta_type{}); +} + TEST_F(MetaType, RemovePointer) { ASSERT_EQ(entt::resolve().remove_pointer(), entt::resolve()); ASSERT_EQ(entt::resolve().remove_pointer(), entt::resolve()); @@ -654,19 +668,6 @@ TEST_F(MetaType, ResetAndReRegistrationAfterReset) { ASSERT_TRUE(entt::resolve().data("rand"_hs).prop(property_t::random)); } -TEST_F(MetaType, ClassTemplate) { - ASSERT_FALSE(entt::resolve().is_template_specialization()); - ASSERT_EQ(entt::resolve().template_arity(), 0u); - ASSERT_EQ(entt::resolve().template_type(), entt::meta_type{}); - ASSERT_EQ(entt::resolve().template_arg(0u), entt::meta_type{}); - - ASSERT_TRUE(entt::resolve>().is_template_specialization()); - ASSERT_EQ(entt::resolve>().template_arity(), 1u); - ASSERT_EQ(entt::resolve>().template_type(), entt::resolve>()); - ASSERT_EQ(entt::resolve>().template_arg(0u), entt::resolve()); - ASSERT_EQ(entt::resolve>().template_arg(1u), entt::meta_type{}); -} - TEST_F(MetaType, ReRegistration) { using namespace entt::literals;