meta: extended template info support
This commit is contained in:
3
TODO
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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
src/entt/meta/template.hpp
Normal file
33
src/entt/meta/template.hpp
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
test/entt/meta/meta_template.cpp
Normal file
49
test/entt/meta/meta_template.cpp
Normal 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{});
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user