#ifndef ENTT_META_UTILITY_HPP #define ENTT_META_UTILITY_HPP #include #include #include #include #include "../core/type_traits.hpp" #include "../locator/locator.hpp" #include "meta.hpp" #include "node.hpp" #include "policy.hpp" namespace entt { /** * @brief Meta function descriptor traits. * @tparam Ret Function return type. * @tparam Args Function arguments. * @tparam Static Function staticness. * @tparam Const Function constness. */ template struct meta_function_descriptor_traits { /*! @brief Meta function return type. */ using return_type = Ret; /*! @brief Meta function arguments. */ using args_type = Args; /*! @brief True if the meta function is static, false otherwise. */ static constexpr bool is_static = Static; /*! @brief True if the meta function is const, false otherwise. */ static constexpr bool is_const = Const; }; /*! @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 : meta_function_descriptor_traits< Ret, std::conditional_t, type_list, type_list>, !std::is_base_of_v, true> {}; /** * @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 : meta_function_descriptor_traits< Ret, std::conditional_t, type_list, type_list>, !std::is_base_of_v, false> {}; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta data is associated. * @tparam Class Actual owner of the data member. * @tparam Ret Data member type. */ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret &, std::conditional_t, type_list<>, type_list>, !std::is_base_of_v, false> {}; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. * @tparam MaybeType First function argument. * @tparam Args Other function arguments. */ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret, std::conditional_t>, Type>, type_list, type_list>, !std::is_base_of_v>, Type>, std::is_base_of_v>, Type> && std::is_const_v>> {}; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. */ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret, type_list<>, true, false> {}; /** * @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 Class::*); template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (*)(Args...)); template static constexpr meta_function_descriptor get_rid_of_noexcept(Class); 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 Wraps a value depending on the given policy. * * This function always returns a wrapped value in the requested context.
* Therefore, if the passed value is itself a wrapped object with a different * context, it undergoes a rebinding to the requested context. * * @tparam Policy Optional policy (no policy set by default). * @tparam Type Type of value to wrap. * @param ctx The context from which to search for meta types. * @param value Value to wrap. * @return A meta any containing the returned value, if any. */ template std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { if constexpr(std::is_same_v) { return meta_any{ctx, std::in_place_type}; } else if constexpr(std::is_same_v) { return meta_any{ctx, std::in_place_type, value}; } else if constexpr(std::is_same_v) { static_assert(std::is_lvalue_reference_v, "Invalid type"); return meta_any{ctx, std::in_place_type &>, std::as_const(value)}; } else { return meta_any{ctx, std::forward(value)}; } } /** * @brief Wraps a value depending on the given policy. * @tparam Policy Optional policy (no policy set by default). * @tparam Type Type of value to wrap. * @param value Value to wrap. * @return A meta any containing the returned value, if any. */ template std::enable_if_t, meta_any> meta_dispatch(Type &&value) { return meta_dispatch(locator::value_or(), std::forward(value)); } /** * @brief Returns the meta type of the i-th element of a list of arguments. * @tparam Type Type list of the actual types of arguments. * @param ctx The context from which to search for meta types. * @param index The index of the element for which to return the meta type. * @return The meta type of the i-th element of the list of arguments. */ template [[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept { auto &&context = internal::meta_context::from(ctx); return {ctx, internal::meta_arg_node(context, Type{}, index)}; } /** * @brief Returns the meta type of the i-th element of a list of arguments. * @tparam Type Type list of the actual types of arguments. * @param index The index of the element for which to return the meta type. * @return The meta type of the i-th element of the list of arguments. */ template [[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept { return meta_arg(locator::value_or(), index); } /** * @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_member_function_pointer_v || std::is_function_v>>) { using descriptor = meta_function_helper_t; using data_type = type_list_element_t; if(auto *const clazz = instance->try_cast(); clazz && 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::return_type>; if constexpr(!std::is_array_v && !std::is_const_v) { if(auto *const clazz = instance->try_cast(); clazz && 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. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @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 ctx The context from which to search for meta types. * @param instance An opaque instance of the underlying type, if required. * @return A meta any containing the value of the underlying variable. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_getter(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance) { if constexpr(std::is_member_pointer_v || std::is_function_v>>) { if constexpr(!std::is_array_v>>>) { if constexpr(std::is_invocable_v) { if(auto *clazz = instance->try_cast(); clazz) { return meta_dispatch(ctx, std::invoke(Data, *clazz)); } } if constexpr(std::is_invocable_v) { if(auto *fallback = instance->try_cast(); fallback) { return meta_dispatch(ctx, std::invoke(Data, *fallback)); } } } return meta_any{meta_ctx_arg, ctx}; } else if constexpr(std::is_pointer_v) { if constexpr(std::is_array_v>) { return meta_any{meta_ctx_arg, ctx}; } else { return meta_dispatch(ctx, *Data); } } else { return meta_dispatch(ctx, Data); } } /** * @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]] std::enable_if_t, meta_any> meta_getter(meta_handle instance) { return meta_getter(locator::value_or(), std::move(instance)); } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) { if constexpr(std::is_same_v(candidate), args...)), void>) { std::invoke(std::forward(candidate), args...); return meta_any{ctx, std::in_place_type}; } else { return meta_dispatch(ctx, std::invoke(std::forward(candidate), args...)); } } template [[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence) { using descriptor = meta_function_helper_t>; if constexpr(std::is_invocable_v, const Type &, type_list_element_t...>) { if(const auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); } } else if constexpr(std::is_invocable_v, Type &, type_list_element_t...>) { if(auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); } } else { if(((args + Index)->allow_cast>() && ...)) { return meta_invoke_with_args(ctx, std::forward(candidate), (args + Index)->cast>()...); } } return meta_any{meta_ctx_arg, ctx}; } template [[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence) { if(((args + Index)->allow_cast() && ...)) { return meta_any{ctx, std::in_place_type, (args + Index)->cast()...}; } return meta_any{meta_ctx_arg, ctx}; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Tries to _invoke_ an object given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @param ctx The context from which to search for meta types. * @tparam Candidate The type of the actual object to _invoke_. * @param instance An opaque instance of the underlying type, if required. * @param candidate The actual object to _invoke_. * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) { return internal::meta_invoke(ctx, std::move(instance), std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); } /** * @brief Tries to _invoke_ an object given a list of erased parameters. * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @tparam Candidate The type of the actual object to _invoke_. * @param instance An opaque instance of the underlying type, if required. * @param candidate The actual object to _invoke_. * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) { return meta_invoke(locator::value_or(), std::move(instance), std::forward(candidate), args); } /** * @brief Tries to invoke a function given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @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). * @param ctx The context from which to search for meta types. * @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]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) { return internal::meta_invoke(ctx, std::move(instance), Candidate, args, std::make_index_sequence>::args_type::size>{}); } /** * @brief Tries to invoke a function given a list of erased parameters. * @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). * @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]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, meta_any *const args) { return meta_invoke(locator::value_or(), std::move(instance), args); } /** * @brief Tries to construct an instance given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Actual type of the instance to construct. * @tparam Args Types of arguments expected. * @param ctx The context from which to search for meta types. * @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(const meta_ctx &ctx, meta_any *const args) { return internal::meta_construct(ctx, args, std::index_sequence_for{}); } /** * @brief Tries to construct an instance given a list of erased parameters. * @tparam Type Actual type of the instance to construct. * @tparam Args Types of arguments expected. * @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) { return meta_construct(locator::value_or(), args); } /** * @brief Tries to construct an instance given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @tparam Candidate The type of the actual object to _invoke_. * @param ctx The context from which to search for meta types. * @param candidate The actual object to _invoke_. * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) { if constexpr(meta_function_helper_t::is_static || std::is_class_v>>) { return internal::meta_invoke(ctx, {}, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); } else { return internal::meta_invoke(ctx, *args, std::forward(candidate), args + 1u, std::make_index_sequence>::args_type::size>{}); } } /** * @brief Tries to construct an instance given a list of erased parameters. * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @tparam Candidate The type of the actual object to _invoke_. * @param candidate The actual object to _invoke_. * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) { return meta_construct(locator::value_or(), std::forward(candidate), args); } /** * @brief Tries to construct an instance given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @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). * @param ctx The context from which to search for meta types. * @param args Parameters to use to invoke the function. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) { return meta_construct(ctx, Candidate, args); } /** * @brief Tries to construct an instance given a list of erased parameters. * @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). * @param args Parameters to use to invoke the function. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_construct(meta_any *const args) { return meta_construct(locator::value_or(), args); } } // namespace entt #endif