meta: extended template info support

This commit is contained in:
Michele Caini
2021-03-11 12:13:20 +01:00
parent a85712e8aa
commit bcaf1489c0
10 changed files with 188 additions and 37 deletions

3
TODO
View File

@@ -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)

View File

@@ -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

View File

@@ -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"

View File

@@ -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];
}
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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{});
}

View File

@@ -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;