diff --git a/src/entt/entt.hpp b/src/entt/entt.hpp index acf0937b2..6be076db9 100644 --- a/src/entt/entt.hpp +++ b/src/entt/entt.hpp @@ -35,6 +35,7 @@ #include "meta/range.hpp" #include "meta/resolve.hpp" #include "meta/type_traits.hpp" +#include "meta/utility.hpp" #include "platform/android-ndk-r17.hpp" #include "poly/poly.hpp" #include "process/process.hpp" diff --git a/src/entt/meta/factory.hpp b/src/entt/meta/factory.hpp index 2bbff1dfc..4f21f28c9 100644 --- a/src/entt/meta/factory.hpp +++ b/src/entt/meta/factory.hpp @@ -2,9 +2,7 @@ #define ENTT_META_FACTORY_HPP -#include #include -#include #include #include #include @@ -14,199 +12,12 @@ #include "../core/type_traits.hpp" #include "meta.hpp" #include "policy.hpp" +#include "utility.hpp" namespace entt { -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - - -namespace internal { - - -template -struct meta_function_helper; - - -template -struct meta_function_helper { - using return_type = Ret; - using args_type = type_list; - - static constexpr auto is_static = Static; - static constexpr auto is_const = Const; -}; - - -template -constexpr meta_function_helper, Ret(Args...), Ret(Class &, Args...)>, true, !std::is_same_v> -to_meta_function_helper(Ret(Class:: *)(Args...) const); - - -template -constexpr meta_function_helper, Ret(Args...), Ret(Class &, Args...)>, false, !std::is_same_v> -to_meta_function_helper(Ret(Class:: *)(Args...)); - - -template -constexpr meta_function_helper -to_meta_function_helper(Ret(*)(Args...)); - - -template -constexpr void to_meta_function_helper(...); - - -template -using meta_function_helper_t = decltype(to_meta_function_helper(std::declval())); - - -template -[[nodiscard]] static auto arg(type_list, const std::size_t index) ENTT_NOEXCEPT { - return std::array{{meta_info::resolve()...}}[index]; -} - - -template -[[nodiscard]] meta_any construct(meta_any * const args, std::index_sequence) { - if(((args+Index)->allow_cast() && ...)) { - return Type{(args+Index)->cast()...}; - } - - return {}; -} - - -template -[[nodiscard]] bool setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { - if constexpr(!std::is_same_v && !std::is_same_v) { - if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { - using helper_type = meta_function_helper_t; - using data_type = type_list_element_t, typename helper_type::args_type>; - - if(auto * const clazz = instance->try_cast(); clazz) { - if(value.allow_cast()) { - std::invoke(Data, *clazz, value.cast()); - return true; - } - } - } else if constexpr(std::is_member_object_pointer_v) { - using data_type = std::remove_reference_t().*Data)>; - - if constexpr(!std::is_array_v && !std::is_const_v) { - if(auto * const clazz = instance->try_cast(); clazz) { - if(value.allow_cast()) { - std::invoke(Data, clazz) = value.cast(); - return true; - } - } - } - } else { - using data_type = std::remove_reference_t; - - if constexpr(!std::is_array_v && !std::is_const_v) { - if(value.allow_cast()) { - *Data = value.cast(); - return true; - } - } - } - } - - return false; -} - - -template -[[nodiscard]] meta_any getter([[maybe_unused]] meta_handle instance) { - [[maybe_unused]] auto dispatch = [](auto &&value) { - if constexpr(std::is_same_v) { - return meta_any{std::in_place_type, std::forward(value)}; - } else if constexpr(std::is_same_v) { - return meta_any{std::reference_wrapper{std::forward(value)}}; - } else if constexpr(std::is_same_v) { - return meta_any{std::cref(std::forward(value))}; - } else { - static_assert(std::is_same_v, "Policy not supported"); - return meta_any{std::forward(value)}; - } - }; - - if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { - auto * const clazz = instance->try_cast, const Type, Type>>(); - return clazz ? dispatch(std::invoke(Data, *clazz)) : meta_any{}; - } else if constexpr(std::is_member_object_pointer_v) { - if constexpr(std::is_array_v().*Data)>>>) { - return meta_any{}; - } else { - if(auto * clazz = instance->try_cast(); clazz) { - return dispatch(std::invoke(Data, *clazz)); - } else { - auto * fallback = instance->try_cast(); - return fallback ? dispatch(std::invoke(Data, *fallback)) : meta_any{}; - } - } - } else if constexpr(std::is_pointer_v) { - if constexpr(std::is_array_v>) { - return meta_any{}; - } else { - return dispatch(*Data); - } - } else { - return dispatch(Data); - } -} - - -template -[[nodiscard]] meta_any invoke([[maybe_unused]] meta_handle instance, meta_any *args, std::index_sequence) { - using helper_type = meta_function_helper_t; - - auto dispatch = [](auto &&... params) { - if constexpr(std::is_void_v> || std::is_same_v) { - std::invoke(Candidate, std::forward(params)...); - return meta_any{std::in_place_type}; - } else if constexpr(std::is_same_v) { - return meta_any{std::reference_wrapper{std::invoke(Candidate, std::forward(params)...)}}; - } else if constexpr(std::is_same_v) { - return meta_any{std::cref(std::invoke(Candidate, std::forward(params)...))}; - } else { - static_assert(std::is_same_v, "Policy not supported"); - return meta_any{std::invoke(Candidate, std::forward(params)...)}; - } - }; - - if constexpr(std::is_invocable_v...>) { - if(const auto * const clazz = instance->try_cast(); clazz && ((args+Index)->allow_cast>() && ...)) { - return dispatch(*clazz, (args+Index)->cast>()...); - } - } else if constexpr(std::is_invocable_v...>) { - if(auto * const clazz = instance->try_cast(); clazz && ((args+Index)->allow_cast>() && ...)) { - return dispatch(*clazz, (args+Index)->cast>()...); - } - } else { - if(((args+Index)->allow_cast>() && ...)) { - return dispatch((args+Index)->cast>()...); - } - } - - return meta_any{}; -} - - -} - - -/** - * Internal details not to be documented. - * @endcond - */ - - /** * @brief Meta factory to be used for reflection purposes. * @@ -477,20 +288,20 @@ public: */ template auto ctor() ENTT_NOEXCEPT { - using helper_type = internal::meta_function_helper_t; - static_assert(std::is_same_v>, Type>, "The function doesn't return an object of the required type"); + using descriptor = meta_function_helper_t; + static_assert(std::is_same_v>, Type>, "The function doesn't return an object of the required type"); auto * const type = internal::meta_info::resolve(); static internal::meta_ctor_node node{ type, nullptr, nullptr, - helper_type::args_type::size, + descriptor::args_type::size, [](const typename internal::meta_ctor_node::size_type index) ENTT_NOEXCEPT { - return internal::arg(typename helper_type::args_type{}, index); + return meta_arg(typename descriptor::args_type{}, index); }, [](meta_any * const any) { - return internal::invoke({}, any, std::make_index_sequence{}); + return meta_invoke({}, any, std::make_index_sequence{}); } }; @@ -513,19 +324,19 @@ public: */ template auto ctor() ENTT_NOEXCEPT { - using helper_type = internal::meta_function_helper_t; + using descriptor = meta_function_helper_t; auto * const type = internal::meta_info::resolve(); static internal::meta_ctor_node node{ type, nullptr, nullptr, - helper_type::args_type::size, + descriptor::args_type::size, [](const typename internal::meta_ctor_node::size_type index) ENTT_NOEXCEPT { - return internal::arg(typename helper_type::args_type{}, index); + return meta_arg(typename descriptor::args_type{}, index); }, [](meta_any * const any) { - return internal::construct(any, std::make_index_sequence{}); + return meta_construct(any, std::make_index_sequence{}); } }; @@ -597,8 +408,8 @@ public: std::is_same_v || std::is_const_v, true, &internal::meta_info::resolve, - &internal::setter, - &internal::getter + &meta_setter, + &meta_getter }; ENTT_ASSERT(!exists(id, type->data)); @@ -644,8 +455,8 @@ public: std::is_same_v || (std::is_member_object_pointer_v && std::is_const_v), false, &internal::meta_info::resolve, - &internal::setter, - &internal::getter + &meta_setter, + &meta_getter }; ENTT_ASSERT(!exists(id, type->data)); @@ -672,7 +483,7 @@ public: */ template auto func(const id_type id) ENTT_NOEXCEPT { - using helper_type = internal::meta_function_helper_t; + using descriptor = meta_function_helper_t; auto * const type = internal::meta_info::resolve(); static internal::meta_func_node node{ @@ -680,15 +491,15 @@ public: type, nullptr, nullptr, - helper_type::args_type::size, - helper_type::is_const, - helper_type::is_static, - &internal::meta_info, void, typename helper_type::return_type>>::resolve, + descriptor::args_type::size, + descriptor::is_const, + descriptor::is_static, + &internal::meta_info, void, typename descriptor::return_type>>::resolve, [](const typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT { - return internal::arg(typename helper_type::args_type{}, index); + return meta_arg(typename descriptor::args_type{}, index); }, [](meta_handle instance, meta_any *args) { - return internal::invoke(std::move(instance), args, std::make_index_sequence{}); + return meta_invoke(std::move(instance), args, std::make_index_sequence{}); } }; diff --git a/src/entt/meta/meta.hpp b/src/entt/meta/meta.hpp index 1aad52749..88ee03dbc 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -70,7 +70,7 @@ struct meta_ctor_node { meta_ctor_node * next; meta_prop_node * prop; const size_type size; - meta_type_node *(* const arg)(const size_type) ENTT_NOEXCEPT; + meta_type(* const arg)(const size_type) ENTT_NOEXCEPT; meta_any(* const invoke)(meta_any * const); }; @@ -98,7 +98,7 @@ struct meta_func_node { const bool is_const; const bool is_static; meta_type_node *(* const ret)() ENTT_NOEXCEPT; - meta_type_node *(* const arg)(const size_type) ENTT_NOEXCEPT; + meta_type(* const arg)(const size_type) ENTT_NOEXCEPT; meta_any(* const invoke)(meta_handle, meta_any *); }; @@ -1172,6 +1172,10 @@ private: /*! @brief Opaque wrapper for types. */ class meta_type { static bool can_cast_or_convert(const internal::meta_type_node *type, const type_info info) ENTT_NOEXCEPT { + if(type->info == info) { + return true; + } + for(const auto *curr = type->conv; curr; curr = curr->next) { if(curr->type()->info == info) { return true; @@ -1179,7 +1183,7 @@ class meta_type { } for(const auto *curr = type->base; curr; curr = curr->next) { - if(auto *target = curr->type(); target->info == info || can_cast_or_convert(target, info)) { + if(auto *target = curr->type(); can_cast_or_convert(target, info)) { return true; } } @@ -1190,7 +1194,7 @@ class meta_type { template [[nodiscard]] static const internal::meta_ctor_node * ctor(const internal::meta_ctor_node *curr, std::index_sequence) { for(; curr; curr = curr->next) { - if(curr->size == sizeof...(Args) && ([](auto *from, auto *to) { return from->info == to->info || can_cast_or_convert(from, to->info); }(internal::meta_info::resolve(), curr->arg(Index)) && ...)) { + if(curr->size == sizeof...(Args) && (can_cast_or_convert(internal::meta_info::resolve(), curr->arg(Index).info()) && ...)) { return curr; } } @@ -1506,7 +1510,7 @@ public: } /** - * @brief Invokes the function with the given identifier, if possible. + * @brief Invokes a function given an identifier, if possible. * * It must be possible to cast the instance to the parent type of the member * function. Otherwise, invoking the underlying function results in an @@ -1531,7 +1535,7 @@ public: for(size_type next{}; next < sz && next == (direct + ext); ++next) { const auto type = args[next].type(); - const auto req = it->arg(next)->info; + const auto req = it->arg(next).info(); type.info() == req ? ++direct : (ext += can_cast_or_convert(type.node, req)); } @@ -1738,7 +1742,7 @@ bool meta_any::set(const id_type id, Type &&value) { [[nodiscard]] inline meta_type meta_ctor::arg(size_type index) const ENTT_NOEXCEPT { - return index < size() ? node->arg(index) : nullptr; + return index < size() ? node->arg(index) : meta_type{}; } @@ -1763,7 +1767,7 @@ bool meta_any::set(const id_type id, Type &&value) { [[nodiscard]] inline meta_type meta_func::arg(size_type index) const ENTT_NOEXCEPT { - return index < size() ? node->arg(index) : nullptr; + return index < size() ? node->arg(index) : meta_type{}; } diff --git a/src/entt/meta/utility.hpp b/src/entt/meta/utility.hpp new file mode 100644 index 000000000..0b03e0459 --- /dev/null +++ b/src/entt/meta/utility.hpp @@ -0,0 +1,296 @@ +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + + +#include +#include +#include +#include +#include +#include "../config/config.h" +#include "../core/type_traits.hpp" +#include "meta.hpp" +#include "policy.hpp" + + +namespace entt { + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct meta_function_descriptor; + + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = std::conditional_t, type_list, type_list>; + + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr auto is_const = true; + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr auto is_static = !std::is_same_v; +}; + + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = std::conditional_t, type_list, type_list>; + + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr auto is_const = false; + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr auto is_static = !std::is_same_v; +}; + + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = type_list; + + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr auto is_const = false; + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr auto is_static = true; +}; + + +/** + * @brief Meta function helper. + * + * Converts a function type to be associated with a reflected type into its meta + * function descriptor. + * + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template +class meta_function_helper { + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(Class:: *)(Args...) const); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(Class:: *)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(*)(Args...)); + +public: + /*! @brief The meta function descriptor of the given function. */ + using type = decltype(get_rid_of_noexcept(std::declval())); +}; + + +/** + * @brief Helper type. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template +using meta_function_helper_t = typename meta_function_helper::type; + + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Args Actual types of arguments. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(type_list, const std::size_t index) ENTT_NOEXCEPT { + return std::array{{internal::meta_info::resolve()...}}[index]; +} + + +/** + * @brief Constructs an instance given a list of erased parameters, if possible. + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @tparam Index Indexes to use to extract erased arguments from their list. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(meta_any * const args, std::index_sequence) { + if(((args+Index)->allow_cast() && ...)) { + return Type{(args+Index)->cast()...}; + } + + return {}; +} + + +/** + * @brief Sets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to set. + * @param instance An opaque instance of the underlying type, if required. + * @param value Parameter to use to set the variable. + * @return True in case of success, false otherwise. + */ +template +[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { + if constexpr(!std::is_same_v && !std::is_same_v) { + if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { + using descriptor = meta_function_helper_t; + using data_type = type_list_element_t, typename descriptor::args_type>; + + if(auto * const clazz = instance->try_cast(); clazz) { + if(value.allow_cast()) { + std::invoke(Data, *clazz, value.cast()); + return true; + } + } + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_reference_t().*Data)>; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(auto * const clazz = instance->try_cast(); clazz) { + if(value.allow_cast()) { + std::invoke(Data, clazz) = value.cast(); + return true; + } + } + } + } else { + using data_type = std::remove_reference_t; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(value.allow_cast()) { + *Data = value.cast(); + return true; + } + } + } + } + + return false; +} + + +/** + * @brief Gets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) { + [[maybe_unused]] auto dispatch = [](auto &&value) { + if constexpr(std::is_same_v) { + return meta_any{std::in_place_type, std::forward(value)}; + } else if constexpr(std::is_same_v) { + return meta_any{std::reference_wrapper{std::forward(value)}}; + } else if constexpr(std::is_same_v) { + return meta_any{std::cref(std::forward(value))}; + } else { + static_assert(std::is_same_v, "Policy not supported"); + return meta_any{std::forward(value)}; + } + }; + + if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { + auto * const clazz = instance->try_cast, const Type, Type>>(); + return clazz ? dispatch(std::invoke(Data, *clazz)) : meta_any{}; + } else if constexpr(std::is_member_object_pointer_v) { + if constexpr(std::is_array_v().*Data)>>>) { + return meta_any{}; + } else { + if(auto * clazz = instance->try_cast(); clazz) { + return dispatch(std::invoke(Data, *clazz)); + } else { + auto * fallback = instance->try_cast(); + return fallback ? dispatch(std::invoke(Data, *fallback)) : meta_any{}; + } + } + } else if constexpr(std::is_pointer_v) { + if constexpr(std::is_array_v>) { + return meta_any{}; + } else { + return dispatch(*Data); + } + } else { + return dispatch(Data); + } +} + + +/** + * @brief Invokes a function given a list of erased parameters, if possible. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Index Indexes to use to extract erased arguments from their list. + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, meta_any *args, std::index_sequence) { + using descriptor = meta_function_helper_t; + + auto dispatch = [](auto &&... params) { + if constexpr(std::is_void_v> || std::is_same_v) { + std::invoke(Candidate, std::forward(params)...); + return meta_any{std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{std::reference_wrapper{std::invoke(Candidate, std::forward(params)...)}}; + } else if constexpr(std::is_same_v) { + return meta_any{std::cref(std::invoke(Candidate, std::forward(params)...))}; + } else { + static_assert(std::is_same_v, "Policy not supported"); + return meta_any{std::invoke(Candidate, std::forward(params)...)}; + } + }; + + if constexpr(std::is_invocable_v...>) { + if(const auto * const clazz = instance->try_cast(); clazz && ((args+Index)->allow_cast>() && ...)) { + return dispatch(*clazz, (args+Index)->cast>()...); + } + } else if constexpr(std::is_invocable_v...>) { + if(auto * const clazz = instance->try_cast(); clazz && ((args+Index)->allow_cast>() && ...)) { + return dispatch(*clazz, (args+Index)->cast>()...); + } + } else { + if(((args+Index)->allow_cast>() && ...)) { + return dispatch((args+Index)->cast>()...); + } + } + + return meta_any{}; +} + + +} + + +#endif