🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

687 lines
24 KiB

#ifndef ENTT_META_FACTORY_HPP
#define ENTT_META_FACTORY_HPP
#include <utility>
#include <algorithm>
#include <type_traits>
#include "../config/config.h"
#include "../core/hashed_string.hpp"
#include "meta.hpp"
namespace entt {
template<typename>
class meta_factory;
template<typename Type, typename... Property>
meta_factory<Type> reflect(const char *str, Property &&... property) ENTT_NOEXCEPT;
template<typename Type>
bool unregister() ENTT_NOEXCEPT;
/**
* @brief A meta factory to be used for reflection purposes.
*
* A meta factory is an utility class used to reflect types, data and functions
* of all sorts. This class ensures that the underlying web of types is built
* correctly and performs some checks in debug mode to ensure that there are no
* subtle errors at runtime.
*
* @tparam Type Reflected type for which the factory was created.
*/
template<typename Type>
class meta_factory {
static_assert(std::is_object_v<Type> && !(std::is_const_v<Type> || std::is_volatile_v<Type>));
template<typename Node>
inline bool duplicate(const hashed_string &name, const Node *node) ENTT_NOEXCEPT {
return node ? node->name == name || duplicate(name, node->next) : false;
}
inline bool duplicate(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT {
return node ? node->key() == key || duplicate(key, node->next) : false;
}
template<typename>
internal::meta_prop_node * properties() {
return nullptr;
}
template<typename Owner, typename Property, typename... Other>
internal::meta_prop_node * properties(Property &&property, Other &&... other) {
static std::remove_cv_t<std::remove_reference_t<Property>> prop{};
static internal::meta_prop_node node{
nullptr,
[]() -> meta_any {
return std::get<0>(prop);
},
[]() -> meta_any {
return std::get<1>(prop);
},
[]() -> meta_prop {
return &node;
}
};
prop = std::forward<Property>(property);
node.next = properties<Owner>(std::forward<Other>(other)...);
ENTT_ASSERT(!duplicate(meta_any{std::get<0>(prop)}, node.next));
return &node;
}
template<typename... Property>
meta_factory type(hashed_string name, Property &&... property) ENTT_NOEXCEPT {
static internal::meta_type_node node{
{},
nullptr,
nullptr,
std::is_void_v<Type>,
std::is_integral_v<Type>,
std::is_floating_point_v<Type>,
std::is_array_v<Type>,
std::is_enum_v<Type>,
std::is_union_v<Type>,
std::is_class_v<Type>,
std::is_pointer_v<Type>,
std::is_function_v<Type>,
std::is_member_object_pointer_v<Type>,
std::is_member_function_pointer_v<Type>,
std::extent_v<Type>,
[]() -> meta_type {
return internal::meta_info<std::remove_pointer_t<Type>>::resolve();
},
&internal::destroy<Type>,
[]() -> meta_type {
return &node;
}
};
node.name = name;
node.next = internal::meta_info<>::type;
node.prop = properties<Type>(std::forward<Property>(property)...);
ENTT_ASSERT(!duplicate(name, node.next));
ENTT_ASSERT(!internal::meta_info<Type>::type);
internal::meta_info<Type>::type = &node;
internal::meta_info<>::type = &node;
return *this;
}
void unregister_prop(internal::meta_prop_node **prop) {
while(*prop) {
auto *node = *prop;
*prop = node->next;
node->next = nullptr;
}
}
void unregister_dtor() {
if(auto node = internal::meta_info<Type>::type->dtor; node) {
internal::meta_info<Type>::type->dtor = nullptr;
*node->underlying = nullptr;
}
}
template<auto Member>
auto unregister_all(int)
-> decltype((internal::meta_info<Type>::type->*Member)->prop, void()) {
while(internal::meta_info<Type>::type->*Member) {
auto node = internal::meta_info<Type>::type->*Member;
internal::meta_info<Type>::type->*Member = node->next;
unregister_prop(&node->prop);
node->next = nullptr;
*node->underlying = nullptr;
}
}
template<auto Member>
void unregister_all(char) {
while(internal::meta_info<Type>::type->*Member) {
auto node = internal::meta_info<Type>::type->*Member;
internal::meta_info<Type>::type->*Member = node->next;
node->next = nullptr;
*node->underlying = nullptr;
}
}
bool unregister() ENTT_NOEXCEPT {
const auto registered = internal::meta_info<Type>::type;
if(registered) {
if(auto *curr = internal::meta_info<>::type; curr == internal::meta_info<Type>::type) {
internal::meta_info<>::type = internal::meta_info<Type>::type->next;
} else {
while(curr && curr->next != internal::meta_info<Type>::type) {
curr = curr->next;
}
if(curr) {
curr->next = internal::meta_info<Type>::type->next;
}
}
unregister_prop(&internal::meta_info<Type>::type->prop);
unregister_all<&internal::meta_type_node::base>(0);
unregister_all<&internal::meta_type_node::conv>(0);
unregister_all<&internal::meta_type_node::ctor>(0);
unregister_all<&internal::meta_type_node::data>(0);
unregister_all<&internal::meta_type_node::func>(0);
unregister_dtor();
internal::meta_info<Type>::type->name = {};
internal::meta_info<Type>::type->next = nullptr;
internal::meta_info<Type>::type = nullptr;
}
return registered;
}
meta_factory() ENTT_NOEXCEPT = default;
public:
template<typename Other, typename... Property>
friend meta_factory<Other> reflect(const char *str, Property &&... property) ENTT_NOEXCEPT;
template<typename Other>
friend bool unregister() ENTT_NOEXCEPT;
/**
* @brief Assigns a meta base to a meta type.
*
* A reflected base class must be a real base class of the reflected type.
*
* @tparam Base Type of the base class to assign to the meta type.
* @return A meta factory for the parent type.
*/
template<typename Base>
meta_factory base() ENTT_NOEXCEPT {
static_assert(std::is_base_of_v<Base, Type>);
auto * const type = internal::meta_info<Type>::resolve();
static internal::meta_base_node node{
&internal::meta_info<Type>::template base<Base>,
type,
nullptr,
&internal::meta_info<Base>::resolve,
[](void *instance) -> void * {
return static_cast<Base *>(static_cast<Type *>(instance));
},
[]() -> meta_base {
return &node;
}
};
node.next = type->base;
ENTT_ASSERT((!internal::meta_info<Type>::template base<Base>));
internal::meta_info<Type>::template base<Base> = &node;
type->base = &node;
return *this;
}
/**
* @brief Assigns a meta conversion function to a meta type.
*
* The given type must be such that an instance of the reflected type can be
* converted to it.
*
* @tparam To Type of the conversion function to assign to the meta type.
* @return A meta factory for the parent type.
*/
template<typename To>
meta_factory conv() ENTT_NOEXCEPT {
static_assert(std::is_convertible_v<Type, To>);
auto * const type = internal::meta_info<Type>::resolve();
static internal::meta_conv_node node{
&internal::meta_info<Type>::template conv<To>,
type,
nullptr,
&internal::meta_info<To>::resolve,
[](void *instance) -> meta_any {
return static_cast<To>(*static_cast<Type *>(instance));
},
[]() -> meta_conv {
return &node;
}
};
node.next = type->conv;
ENTT_ASSERT((!internal::meta_info<Type>::template conv<To>));
internal::meta_info<Type>::template conv<To> = &node;
type->conv = &node;
return *this;
}
/**
* @brief Assigns a meta constructor to a meta type.
*
* Free functions can be assigned to meta types in the role of
* constructors. All that is required is that they return an instance of the
* underlying type.<br/>
* From a client's point of view, nothing changes if a constructor of a meta
* type is a built-in one or a free function.
*
* @tparam Func The actual function to use as a constructor.
* @tparam Property Types of properties to assign to the meta data.
* @param property Properties to assign to the meta data.
* @return A meta factory for the parent type.
*/
template<auto Func, typename... Property>
meta_factory ctor(Property &&... property) ENTT_NOEXCEPT {
using helper_type = internal::meta_function_helper<std::integral_constant<decltype(Func), Func>>;
static_assert(std::is_same_v<typename helper_type::return_type, Type>);
auto * const type = internal::meta_info<Type>::resolve();
static internal::meta_ctor_node node{
&internal::meta_info<Type>::template ctor<typename helper_type::args_type>,
type,
nullptr,
nullptr,
helper_type::size,
&helper_type::arg,
[](meta_any * const any) {
return internal::invoke<Type, Func>(nullptr, any, std::make_index_sequence<helper_type::size>{});
},
[]() -> meta_ctor {
return &node;
}
};
node.next = type->ctor;
node.prop = properties<typename helper_type::args_type>(std::forward<Property>(property)...);
ENTT_ASSERT((!internal::meta_info<Type>::template ctor<typename helper_type::args_type>));
internal::meta_info<Type>::template ctor<typename helper_type::args_type> = &node;
type->ctor = &node;
return *this;
}
/**
* @brief Assigns a meta constructor to a meta type.
*
* A meta constructor is uniquely identified by the types of its arguments
* and is such that there exists an actual constructor of the underlying
* type that can be invoked with parameters whose types are those given.
*
* @tparam Args Types of arguments to use to construct an instance.
* @tparam Property Types of properties to assign to the meta data.
* @param property Properties to assign to the meta data.
* @return A meta factory for the parent type.
*/
template<typename... Args, typename... Property>
meta_factory ctor(Property &&... property) ENTT_NOEXCEPT {
using helper_type = internal::meta_function_helper<Type(Args...)>;
auto * const type = internal::meta_info<Type>::resolve();
static internal::meta_ctor_node node{
&internal::meta_info<Type>::template ctor<typename helper_type::args_type>,
type,
nullptr,
nullptr,
helper_type::size,
&helper_type::arg,
[](meta_any * const any) {
return internal::construct<Type, Args...>(any, std::make_index_sequence<helper_type::size>{});
},
[]() -> meta_ctor {
return &node;
}
};
node.next = type->ctor;
node.prop = properties<typename helper_type::args_type>(std::forward<Property>(property)...);
ENTT_ASSERT((!internal::meta_info<Type>::template ctor<typename helper_type::args_type>));
internal::meta_info<Type>::template ctor<typename helper_type::args_type> = &node;
type->ctor = &node;
return *this;
}
/**
* @brief Assigns a meta destructor to a meta type.
*
* Free functions can be assigned to meta types in the role of destructors.
* The signature of the function should identical to the following:
*
* @code{.cpp}
* void(Type *);
* @endcode
*
* From a client's point of view, nothing changes if the destructor of a
* meta type is the default one or a custom one.
*
* @tparam Func The actual function to use as a destructor.
* @return A meta factory for the parent type.
*/
template<auto *Func>
meta_factory dtor() ENTT_NOEXCEPT {
static_assert(std::is_invocable_v<decltype(Func), Type *>);
auto * const type = internal::meta_info<Type>::resolve();
static internal::meta_dtor_node node{
&internal::meta_info<Type>::template dtor<Func>,
type,
[](meta_handle handle) {
return handle.type() == internal::meta_info<Type>::resolve()->meta()
? ((*Func)(static_cast<Type *>(handle.data())), true)
: false;
},
[]() -> meta_dtor {
return &node;
}
};
ENTT_ASSERT(!internal::meta_info<Type>::type->dtor);
ENTT_ASSERT((!internal::meta_info<Type>::template dtor<Func>));
internal::meta_info<Type>::template dtor<Func> = &node;
internal::meta_info<Type>::type->dtor = &node;
return *this;
}
/**
* @brief Assigns a meta data to a meta type.
*
* Both data members and static and global variables, as well as constants
* of any kind, can be assigned to a meta type.<br/>
* From a client's point of view, all the variables associated with the
* reflected object will appear as if they were part of the type itself.
*
* @tparam Data The actual variable to attach to the meta type.
* @tparam Property Types of properties to assign to the meta data.
* @param str The name to assign to the meta data.
* @param property Properties to assign to the meta data.
* @return A meta factory for the parent type.
*/
template<auto Data, typename... Property>
meta_factory data(const char *str, Property &&... property) ENTT_NOEXCEPT {
auto * const type = internal::meta_info<Type>::resolve();
internal::meta_data_node *curr = nullptr;
if constexpr(std::is_same_v<Type, decltype(Data)>) {
static internal::meta_data_node node{
&internal::meta_info<Type>::template data<Data>,
{},
type,
nullptr,
nullptr,
true,
true,
&internal::meta_info<Type>::resolve,
[](meta_handle, meta_any, meta_any) { return false; },
[](meta_handle, meta_any) -> meta_any { return Data; },
[]() -> meta_data {
return &node;
}
};
node.prop = properties<std::integral_constant<Type, Data>>(std::forward<Property>(property)...);
curr = &node;
} else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
using data_type = std::remove_reference_t<decltype(std::declval<Type>().*Data)>;
static internal::meta_data_node node{
&internal::meta_info<Type>::template data<Data>,
{},
type,
nullptr,
nullptr,
std::is_const_v<data_type>,
!std::is_member_object_pointer_v<decltype(Data)>,
&internal::meta_info<data_type>::resolve,
&internal::setter<std::is_const_v<data_type>, Type, Data>,
&internal::getter<Type, Data>,
[]() -> meta_data {
return &node;
}
};
node.prop = properties<std::integral_constant<decltype(Data), Data>>(std::forward<Property>(property)...);
curr = &node;
} else {
static_assert(std::is_pointer_v<decltype(Data)>);
using data_type = std::remove_pointer_t<decltype(Data)>;
static internal::meta_data_node node{
&internal::meta_info<Type>::template data<Data>,
{},
type,
nullptr,
nullptr,
std::is_const_v<data_type>,
!std::is_member_object_pointer_v<decltype(Data)>,
&internal::meta_info<data_type>::resolve,
&internal::setter<std::is_const_v<data_type>, Type, Data>,
&internal::getter<Type, Data>,
[]() -> meta_data {
return &node;
}
};
node.prop = properties<std::integral_constant<decltype(Data), Data>>(std::forward<Property>(property)...);
curr = &node;
}
curr->name = hashed_string{str};
curr->next = type->data;
ENTT_ASSERT(!duplicate(hashed_string{str}, curr->next));
ENTT_ASSERT((!internal::meta_info<Type>::template data<Data>));
internal::meta_info<Type>::template data<Data> = curr;
type->data = curr;
return *this;
}
/**
* @brief Assigns a meta data to a meta type by means of its setter and
* getter.
*
* Setters and getters can be either free functions, member functions or a
* mix of them.<br/>
* In case of free functions, setters and getters must accept a pointer to
* an instance of the parent type as their first argument. A setter has then
* an extra argument of a type convertible to that of the parameter to
* set.<br/>
* In case of member functions, getters have no arguments at all, while
* setters has an argument of a type convertible to that of the parameter to
* set.
*
* @tparam Setter The actual function to use as a setter.
* @tparam Getter The actual function to use as a getter.
* @tparam Property Types of properties to assign to the meta data.
* @param str The name to assign to the meta data.
* @param property Properties to assign to the meta data.
* @return A meta factory for the parent type.
*/
template<auto Setter, auto Getter, typename... Property>
meta_factory data(const char *str, Property &&... property) ENTT_NOEXCEPT {
using owner_type = std::tuple<std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>;
using underlying_type = std::invoke_result_t<decltype(Getter), Type *>;
static_assert(std::is_invocable_v<decltype(Setter), Type *, underlying_type>);
auto * const type = internal::meta_info<Type>::resolve();
static internal::meta_data_node node{
&internal::meta_info<Type>::template data<Setter, Getter>,
{},
type,
nullptr,
nullptr,
false,
false,
&internal::meta_info<underlying_type>::resolve,
&internal::setter<false, Type, Setter>,
&internal::getter<Type, Getter>,
[]() -> meta_data {
return &node;
}
};
node.name = hashed_string{str};
node.next = type->data;
node.prop = properties<owner_type>(std::forward<Property>(property)...);
ENTT_ASSERT(!duplicate(hashed_string{str}, node.next));
ENTT_ASSERT((!internal::meta_info<Type>::template data<Setter, Getter>));
internal::meta_info<Type>::template data<Setter, Getter> = &node;
type->data = &node;
return *this;
}
/**
* @brief Assigns a meta funcion to a meta type.
*
* Both member functions and free functions can be assigned to a meta
* type.<br/>
* From a client's point of view, all the functions associated with the
* reflected object will appear as if they were part of the type itself.
*
* @tparam Func The actual function to attach to the meta type.
* @tparam Property Types of properties to assign to the meta function.
* @param str The name to assign to the meta function.
* @param property Properties to assign to the meta function.
* @return A meta factory for the parent type.
*/
template<auto Func, typename... Property>
meta_factory func(const char *str, Property &&... property) ENTT_NOEXCEPT {
using owner_type = std::integral_constant<decltype(Func), Func>;
using func_type = internal::meta_function_helper<std::integral_constant<decltype(Func), Func>>;
auto * const type = internal::meta_info<Type>::resolve();
static internal::meta_func_node node{
&internal::meta_info<Type>::template func<Func>,
{},
type,
nullptr,
nullptr,
func_type::size,
func_type::is_const,
func_type::is_static,
&internal::meta_info<typename func_type::return_type>::resolve,
&func_type::arg,
[](meta_handle handle, meta_any *any) {
return internal::invoke<Type, Func>(handle, any, std::make_index_sequence<func_type::size>{});
},
[]() -> meta_func {
return &node;
}
};
node.name = hashed_string{str};
node.next = type->func;
node.prop = properties<owner_type>(std::forward<Property>(property)...);
ENTT_ASSERT(!duplicate(hashed_string{str}, node.next));
ENTT_ASSERT((!internal::meta_info<Type>::template func<Func>));
internal::meta_info<Type>::template func<Func> = &node;
type->func = &node;
return *this;
}
};
/**
* @brief Basic function to use for reflection.
*
* This is the point from which everything starts.<br/>
* By invoking this function with a type that is not yet reflected, a meta type
* is created to which it will be possible to attach data and functions through
* a dedicated factory.
*
* @tparam Type Type to reflect.
* @tparam Property Types of properties to assign to the reflected type.
* @param str The name to assign to the reflected type.
* @param property Properties to assign to the reflected type.
* @return A meta factory for the given type.
*/
template<typename Type, typename... Property>
inline meta_factory<Type> reflect(const char *str, Property &&... property) ENTT_NOEXCEPT {
return meta_factory<Type>{}.type(hashed_string{str}, std::forward<Property>(property)...);
}
/**
* @brief Basic function to use for reflection.
*
* This is the point from which everything starts.<br/>
* By invoking this function with a type that is not yet reflected, a meta type
* is created to which it will be possible to attach data and functions through
* a dedicated factory.
*
* @tparam Type Type to reflect.
* @return A meta factory for the given type.
*/
template<typename Type>
inline meta_factory<Type> reflect() ENTT_NOEXCEPT {
return meta_factory<Type>{};
}
/**
* @brief Basic function to unregister a type.
*
* This function unregisters a type and all its data members, member functions
* and properties, as well as its constructors, destructors and conversion
* functions if any.<br/>
* Base classes aren't unregistered but the link between the two types is
* removed.
*
* @tparam Type Type to unregister.
* @return True if the type to unregister exists, false otherwise.
*/
template<typename Type>
inline bool unregister() ENTT_NOEXCEPT {
return meta_factory<Type>().unregister();
}
/**
* @brief Returns the meta type associated with a given type.
* @tparam Type Type to use to search for a meta type.
* @return The meta type associated with the given type, if any.
*/
template<typename Type>
inline meta_type resolve() ENTT_NOEXCEPT {
return internal::meta_info<Type>::resolve()->meta();
}
/**
* @brief Returns the meta type associated with a given name.
* @param str The name to use to search for a meta type.
* @return The meta type associated with the given name, if any.
*/
inline meta_type resolve(const char *str) ENTT_NOEXCEPT {
const auto *curr = internal::find_if([name = hashed_string{str}](auto *node) {
return node->name == name;
}, internal::meta_info<>::type);
return curr ? curr->meta() : meta_type{};
}
/**
* @brief Iterates all the reflected types.
* @tparam Op Type of the function object to invoke.
* @param op A valid function object.
*/
template<typename Op>
void resolve(Op op) ENTT_NOEXCEPT {
internal::iterate([op = std::move(op)](auto *node) {
op(node->meta());
}, internal::meta_info<>::type);
}
}
#endif // ENTT_META_FACTORY_HPP