Просмотр исходного кода

meta:
* meta_any: convert -> allow_cast
* support for const references
* as_cref_t policy

Michele Caini 5 лет назад
Родитель
Сommit
c52f16f5b8

+ 5 - 1
TODO

@@ -15,10 +15,12 @@
   - spatial query
   - runtime types pool
   - off-line/off-memory/remote
-  - poly
   - ...
 
 WIP:
+* HP: any/meta_any/poly: ::REF -> ::CREF?
+* HP: meta, support for custom deref function other than operator*
+* HP: meta, as_cref_t other than as_ref_t
 * HP: headless (sparse set only) view
 * HP: pass the registry to pools, basic poly storage should have only component member
 * HP: make view pack work also with groups, make packs input iterator only, add view adapter for external sources
@@ -27,6 +29,8 @@ WIP:
 * HP: any/poly: configurable sbo size, compile-time policies like sbo-required.
 * HP: registry: use a poly object for pools, no more pool_data type.
 * HP: make runtime views use opaque storage and therefore return also elements.
+* HP: meta_sequence_container & Co can be replaced by a poly object.
+* HP: poly: support for data members
 * suppress warnings in meta.hpp (uninitialized members)
 * add exclude-only views to combine with packs
 * deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views

+ 14 - 12
docs/md/meta.md

@@ -215,9 +215,9 @@ const bool equal = (any == other);
 Also, `meta_any` adds support for containers and pointer-like types (see the
 following sections for more details).<br/>
 Similar to `any`, this class can also be used to create _aliases_ for unmanaged
-objects either upon construction using `std::ref` or from an existing instance
-by means of the `as_ref` function. However, unlike `any`,` meta_any` treats an
-empty instance and one initialized with `void` differently:
+objects either upon construction using `std::ref` and `std::cref` or from an
+existing instance by means of the `as_ref` function. However, unlike `any`,
+`meta_any` treats an empty instance and one initialized with `void` differently:
 
 ```cpp
 entt::meta_any empty{};
@@ -228,9 +228,10 @@ While `any` treats both objects as empty, `meta_any` treats objects initialized
 with `void` as if they were _valid_ ones. This allows to differentiate between
 failed function calls and function calls that are successful but return
 nothing.<br/>
-Finally, the member functions `try_cast`, `cast` and `convert` are used to know
-if the underlying object has a given type as a base or if it can be converted
-implicitly to it. There is in fact no `any_cast` equivalent for `meta_any`.
+Finally, the member functions `try_cast`, `cast` and `allow_cast` are used to
+cast the underlying object to a given type (either a reference or a value type)
+or to _convert_ a `meta_any` in such a way that a cast becomes viable for the
+resulting object. There is in fact no `any_cast` equivalent for `meta_any`.
 
 ## Enjoy the runtime
 
@@ -684,13 +685,14 @@ There are a few alternatives available at the moment:
   entt::meta<my_type>().func<&my_type::member_function, entt::as_void_t>("member"_hs);
   ```
 
-* The _as-ref_ policy, associated with the type `entt::as_ref_t`.<br/>
-  It allows to build wrappers that act as references to unmanaged objects.
-  Modifying the object contained in the wrapper for which the _reference_ was
-  requested will make it possible to directly modify the instance used to
+* The _as-ref_ and _as-cref_ policies, associated with the types
+  `entt::as_ref_t` and `entt::as_cref_t`.<br/>
+  They allow to build wrappers that act as references to unmanaged objects.
+  Accessing the object contained in the wrapper for which the _reference_ was
+  requested will make it possible to directly access the instance used to
   initialize the wrapper itself.<br/>
-  This policy works with constructors (for example, when objects are taken from
-  an external container rather than created on demand), data members and
+  These policies work with constructors (for example, when objects are taken
+  from an external container rather than created on demand), data members and
   functions in general (as long as their return types are lvalue references).
 
   As an example of use:

+ 39 - 39
src/entt/meta/container.hpp

@@ -273,12 +273,12 @@ struct fixed_sequence_container {
 template<typename Type, typename... Args>
 struct meta_sequence_container_traits<std::vector<Type, Args...>>
         : container_traits<
-            std::vector<Type, Args...>,
-            basic_container,
-            basic_dynamic_container,
-            basic_sequence_container,
-            dynamic_sequence_container
-        >
+              std::vector<Type, Args...>,
+              basic_container,
+              basic_dynamic_container,
+              basic_sequence_container,
+              dynamic_sequence_container
+          >
 {};
 
 
@@ -290,11 +290,11 @@ struct meta_sequence_container_traits<std::vector<Type, Args...>>
 template<typename Type, auto N>
 struct meta_sequence_container_traits<std::array<Type, N>>
         : container_traits<
-            std::array<Type, N>,
-            basic_container,
-            basic_sequence_container,
-            fixed_sequence_container
-        >
+              std::array<Type, N>,
+              basic_container,
+              basic_sequence_container,
+              fixed_sequence_container
+          >
 {};
 
 
@@ -307,13 +307,13 @@ struct meta_sequence_container_traits<std::array<Type, N>>
 template<typename Key, typename Value, typename... Args>
 struct meta_associative_container_traits<std::map<Key, Value, Args...>>
         : container_traits<
-            std::map<Key, Value, Args...>,
-            basic_container,
-            basic_associative_container,
-            basic_dynamic_container,
-            basic_dynamic_associative_container,
-            dynamic_associative_key_value_container
-        >
+              std::map<Key, Value, Args...>,
+              basic_container,
+              basic_associative_container,
+              basic_dynamic_container,
+              basic_dynamic_associative_container,
+              dynamic_associative_key_value_container
+          >
 {
     /*! @brief Mapped type of the sequence container. */
     using mapped_type = typename std::map<Key, Value, Args...>::mapped_type;
@@ -330,13 +330,13 @@ struct meta_associative_container_traits<std::map<Key, Value, Args...>>
 template<typename Key, typename Value, typename... Args>
 struct meta_associative_container_traits<std::unordered_map<Key, Value, Args...>>
         : container_traits<
-            std::unordered_map<Key, Value, Args...>,
-            basic_container,
-            basic_associative_container,
-            basic_dynamic_container,
-            basic_dynamic_associative_container,
-            dynamic_associative_key_value_container
-        >
+              std::unordered_map<Key, Value, Args...>,
+              basic_container,
+              basic_associative_container,
+              basic_dynamic_container,
+              basic_dynamic_associative_container,
+              dynamic_associative_key_value_container
+          >
 {
     /*! @brief Mapped type of the sequence container. */
     using mapped_type = typename std::unordered_map<Key, Value, Args...>::mapped_type;
@@ -351,13 +351,13 @@ struct meta_associative_container_traits<std::unordered_map<Key, Value, Args...>
 template<typename Key, typename... Args>
 struct meta_associative_container_traits<std::set<Key, Args...>>
         : container_traits<
-            std::set<Key, Args...>,
-            basic_container,
-            basic_associative_container,
-            basic_dynamic_container,
-            basic_dynamic_associative_container,
-            dynamic_associative_key_only_container
-        >
+              std::set<Key, Args...>,
+              basic_container,
+              basic_associative_container,
+              basic_dynamic_container,
+              basic_dynamic_associative_container,
+              dynamic_associative_key_only_container
+          >
 {};
 
 
@@ -370,13 +370,13 @@ struct meta_associative_container_traits<std::set<Key, Args...>>
 template<typename Key, typename... Args>
 struct meta_associative_container_traits<std::unordered_set<Key, Args...>>
         : container_traits<
-            std::unordered_set<Key, Args...>,
-            basic_container,
-            basic_associative_container,
-            basic_dynamic_container,
-            basic_dynamic_associative_container,
-            dynamic_associative_key_only_container
-        >
+              std::unordered_set<Key, Args...>,
+              basic_container,
+              basic_associative_container,
+              basic_dynamic_container,
+              basic_dynamic_associative_container,
+              dynamic_associative_key_only_container
+          >
 {};
 
 

+ 52 - 48
src/entt/meta/factory.hpp

@@ -35,8 +35,8 @@ struct meta_function_helper;
 
 template<typename Ret, typename... Args, bool Const, bool Static>
 struct meta_function_helper<Ret(Args...), Const, Static> {
-    using return_type = std::remove_cv_t<std::remove_reference_t<Ret>>;
-    using args_type = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;
+    using return_type = Ret;
+    using args_type = type_list<Args...>;
 
     static constexpr auto is_static = Static;
     static constexpr auto is_const = Const;
@@ -48,12 +48,12 @@ struct meta_function_helper<Ret(Args...), Const, Static> {
 
 
 template<typename Type, typename Ret, typename... Args, typename Class>
-constexpr meta_function_helper<std::conditional_t<std::is_same_v<Type, Class>, Ret(Args...), Ret(std::add_lvalue_reference_t<Class>, Args...)>, true, !std::is_same_v<Type, Class>>
+constexpr meta_function_helper<std::conditional_t<std::is_same_v<Type, Class>, Ret(Args...), Ret(Class &, Args...)>, true, !std::is_same_v<Type, Class>>
 to_meta_function_helper(Ret(Class:: *)(Args...) const);
 
 
 template<typename Type, typename Ret, typename... Args, typename Class>
-constexpr meta_function_helper<std::conditional_t<std::is_same_v<Type, Class>, Ret(Args...), Ret(std::add_lvalue_reference_t<Class>, Args...)>, false, !std::is_same_v<Type, Class>>
+constexpr meta_function_helper<std::conditional_t<std::is_same_v<Type, Class>, Ret(Args...), Ret(Class &, Args...)>, false, !std::is_same_v<Type, Class>>
 to_meta_function_helper(Ret(Class:: *)(Args...));
 
 
@@ -70,12 +70,13 @@ template<typename Type, typename Candidate>
 using meta_function_helper_t = decltype(to_meta_function_helper<Type>(std::declval<Candidate>()));
 
 
-template<typename Type, typename... Args, std::size_t... Indexes>
-[[nodiscard]] meta_any construct(meta_any * const args, std::index_sequence<Indexes...>) {
-    [[maybe_unused]] auto direct = std::make_tuple((args+Indexes)->try_cast<Args>()...);
-    return ((std::get<Indexes>(direct) || (args+Indexes)->convert<Args>()) && ...)
-            ? Type{(std::get<Indexes>(direct) ? *std::get<Indexes>(direct) : (args+Indexes)->cast<Args>())...}
-            : meta_any{};
+template<typename Type, typename... Args, std::size_t... Index>
+[[nodiscard]] meta_any construct(meta_any * const args, std::index_sequence<Index...>) {
+    if(((args+Index)->allow_cast<Args>() && ...)) {
+        return Type{(args+Index)->cast<Args>()...};
+    }
+
+    return {};
 }
 
 
@@ -85,11 +86,11 @@ template<typename Type, auto Data>
 
     if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>> || std::is_member_function_pointer_v<decltype(Data)>) {
         using helper_type = meta_function_helper_t<Type, decltype(Data)>;
-        using data_type = std::tuple_element_t<!std::is_member_function_pointer_v<decltype(Data)>, typename helper_type::args_type>;
+        using data_type = type_list_element_t<!std::is_member_function_pointer_v<decltype(Data)>, typename helper_type::args_type>;
 
         if(auto * const clazz = instance->try_cast<Type>(); clazz) {
-            if(auto * const direct = value.try_cast<data_type>(); direct || value.convert<data_type>()) {
-                std::invoke(Data, *clazz, direct ? *direct : value.cast<data_type>());
+            if(value.allow_cast<data_type>()) {
+                std::invoke(Data, *clazz, value.cast<data_type>());
                 accepted = true;
             }
         }
@@ -98,8 +99,8 @@ template<typename Type, auto Data>
 
         if constexpr(!std::is_array_v<data_type>) {
             if(auto * const clazz = instance->try_cast<Type>(); clazz) {
-                if(auto * const direct = value.try_cast<data_type>(); direct || value.convert<data_type>()) {
-                    std::invoke(Data, clazz) = (direct ? *direct : value.cast<data_type>());
+                if(value.allow_cast<data_type>()) {
+                    std::invoke(Data, clazz) = value.cast<data_type>();
                     accepted = true;
                 }
             }
@@ -108,8 +109,8 @@ template<typename Type, auto Data>
         using data_type = std::remove_cv_t<std::remove_reference_t<decltype(*Data)>>;
 
         if constexpr(!std::is_array_v<data_type>) {
-            if(auto * const direct = value.try_cast<data_type>(); direct || value.convert<data_type>()) {
-                *Data = (direct ? *direct : value.cast<data_type>());
+            if(value.allow_cast<data_type>()) {
+                *Data = value.cast<data_type>();
                 accepted = true;
             }
         }
@@ -126,6 +127,8 @@ template<typename Type, auto Data, typename Policy>
             return meta_any{std::in_place_type<void>, std::forward<decltype(value)>(value)};
         } else if constexpr(std::is_same_v<Policy, as_ref_t>) {
             return meta_any{std::ref(std::forward<decltype(value)>(value))};
+        } else if constexpr(std::is_same_v<Policy, as_cref_t>) {
+            return meta_any{std::cref(std::forward<decltype(value)>(value))};
         } else {
             static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported");
             return meta_any{std::forward<decltype(value)>(value)};
@@ -133,13 +136,13 @@ template<typename Type, auto Data, typename Policy>
     };
 
     if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>> || std::is_member_function_pointer_v<decltype(Data)>) {
-        auto * const clazz = instance->try_cast<Type>();
+        auto * const clazz = instance->try_cast<std::conditional_t<std::is_invocable_v<decltype(Data), const Type *>, const Type, Type>>();
         return clazz ? dispatch(std::invoke(Data, *clazz)) : meta_any{};
     } else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
         if constexpr(std::is_array_v<std::remove_cv_t<std::remove_reference_t<decltype(std::declval<Type>().*Data)>>>) {
             return meta_any{};
         } else {
-            auto * const clazz = instance->try_cast<Type>();
+            auto * const clazz = instance->try_cast<std::conditional_t<std::is_same_v<Policy, as_ref_t>, Type, const Type>>();
             return clazz ? dispatch(std::invoke(Data, clazz)) : meta_any{};
         }
     } else if constexpr(std::is_pointer_v<std::decay_t<decltype(Data)>>) {
@@ -154,38 +157,39 @@ template<typename Type, auto Data, typename Policy>
 }
 
 
-template<typename Type, auto Candidate, typename Policy, std::size_t... Indexes>
-[[nodiscard]] meta_any invoke([[maybe_unused]] meta_handle instance, meta_any *args, std::index_sequence<Indexes...>) {
+template<typename Type, auto Candidate, typename Policy, std::size_t... Index>
+[[nodiscard]] meta_any invoke([[maybe_unused]] meta_handle instance, meta_any *args, std::index_sequence<Index...>) {
     using helper_type = meta_function_helper_t<Type, decltype(Candidate)>;
 
-    auto dispatch = [](auto *... params) {
-        if constexpr(std::is_void_v<typename helper_type::return_type> || std::is_same_v<Policy, as_void_t>) {
-            std::invoke(Candidate, *params...);
+    auto dispatch = [](auto &&... params) {
+        if constexpr(std::is_void_v<std::remove_cv_t<typename helper_type::return_type>> || std::is_same_v<Policy, as_void_t>) {
+            std::invoke(Candidate, std::forward<decltype(params)>(params)...);
             return meta_any{std::in_place_type<void>};
         } else if constexpr(std::is_same_v<Policy, as_ref_t>) {
-            return meta_any{std::ref(std::invoke(Candidate, *params...))};
+            return meta_any{std::ref(std::invoke(Candidate, std::forward<decltype(params)>(params)...))};
+        } else if constexpr(std::is_same_v<Policy, as_cref_t>) {
+            return meta_any{std::cref(std::invoke(Candidate, std::forward<decltype(params)>(params)...))};
         } else {
             static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported");
-            return meta_any{std::invoke(Candidate, *params...)};
+            return meta_any{std::invoke(Candidate, std::forward<decltype(params)>(params)...)};
         }
     };
 
-    [[maybe_unused]] const auto direct = std::make_tuple([](meta_any *any, auto *value) {
-        using arg_type = std::remove_reference_t<decltype(*value)>;
-
-        if(!value && any->convert<arg_type>()) {
-            value = any->try_cast<arg_type>();
+    if constexpr(std::is_invocable_v<decltype(Candidate), const Type &, type_list_element_t<Index, typename helper_type::args_type>...>) {
+        if(const auto * const clazz = instance->try_cast<const Type>(); clazz && ((args+Index)->allow_cast<type_list_element_t<Index, typename helper_type::args_type>>() && ...)) {
+            return dispatch(*clazz, (args+Index)->cast<type_list_element_t<Index, typename helper_type::args_type>>()...);
+        }
+    } else if constexpr(std::is_invocable_v<decltype(Candidate), Type &, type_list_element_t<Index, typename helper_type::args_type>...>) {
+        if(auto * const clazz = instance->try_cast<Type>(); clazz && ((args+Index)->allow_cast<type_list_element_t<Index, typename helper_type::args_type>>() && ...)) {
+            return dispatch(*clazz, (args+Index)->cast<type_list_element_t<Index, typename helper_type::args_type>>()...);
         }
-
-        return value;
-    }(args+Indexes, (args+Indexes)->try_cast<std::tuple_element_t<Indexes, typename helper_type::args_type>>())...);
-
-    if constexpr(std::is_invocable_v<decltype(Candidate), std::add_lvalue_reference_t<Type>, std::tuple_element_t<Indexes, typename helper_type::args_type>...>) {
-        auto * const clazz = instance->try_cast<Type>();
-        return (clazz && (std::get<Indexes>(direct) && ...)) ? dispatch(clazz, std::get<Indexes>(direct)...) : meta_any{};
     } else {
-        return (std::get<Indexes>(direct) && ...) ? dispatch(std::get<Indexes>(direct)...) : meta_any{};
+        if(((args+Index)->allow_cast<type_list_element_t<Index, typename helper_type::args_type>>() && ...)) {
+            return dispatch((args+Index)->cast<type_list_element_t<Index, typename helper_type::args_type>>()...);
+        }
     }
+
+    return meta_any{};
 }
 
 
@@ -469,17 +473,17 @@ public:
     template<auto Candidate, typename Policy = as_is_t>
     auto ctor() ENTT_NOEXCEPT {
         using helper_type = internal::meta_function_helper_t<Type, decltype(Candidate)>;
-        static_assert(std::is_same_v<typename helper_type::return_type, Type>, "The function doesn't return an object of the required type");
+        static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename helper_type::return_type>>, Type>, "The function doesn't return an object of the required type");
         auto * const type = internal::meta_info<Type>::resolve();
 
         static internal::meta_ctor_node node{
             type,
             nullptr,
             nullptr,
-            std::tuple_size_v<typename helper_type::args_type>,
+            helper_type::args_type::size,
             &helper_type::arg,
             [](meta_any * const any) {
-                return internal::invoke<Type, Candidate, Policy>({}, any, std::make_index_sequence<std::tuple_size_v<typename helper_type::args_type>>{});
+                return internal::invoke<Type, Candidate, Policy>({}, any, std::make_index_sequence<helper_type::args_type::size>{});
             }
         };
 
@@ -509,10 +513,10 @@ public:
             type,
             nullptr,
             nullptr,
-            std::tuple_size_v<typename helper_type::args_type>,
+            helper_type::args_type::size,
             &helper_type::arg,
             [](meta_any * const any) {
-                return internal::construct<Type, std::remove_cv_t<std::remove_reference_t<Args>>...>(any, std::make_index_sequence<std::tuple_size_v<typename helper_type::args_type>>{});
+                return internal::construct<Type, Args...>(any, std::make_index_sequence<helper_type::args_type::size>{});
             }
         };
 
@@ -583,7 +587,7 @@ public:
                 nullptr,
                 true,
                 &internal::meta_info<data_type>::resolve,
-                []() -> std::remove_const_t<decltype(internal::meta_data_node::set)> {
+                []() -> std::remove_cv_t<decltype(internal::meta_data_node::set)> {
                     if constexpr(std::is_same_v<Type, data_type> || std::is_const_v<data_type>) {
                         return nullptr;
                     } else {
@@ -635,7 +639,7 @@ public:
             nullptr,
             false,
             &internal::meta_info<underlying_type>::resolve,
-            []() -> std::remove_const_t<decltype(internal::meta_data_node::set)> {
+            []() -> std::remove_cv_t<decltype(internal::meta_data_node::set)> {
                 if constexpr(std::is_same_v<decltype(Setter), std::nullptr_t> || (std::is_member_object_pointer_v<decltype(Setter)> && std::is_const_v<underlying_type>)) {
                     return nullptr;
                 } else {
@@ -677,13 +681,13 @@ public:
             type,
             nullptr,
             nullptr,
-            std::tuple_size_v<typename helper_type::args_type>,
+            helper_type::args_type::size,
             helper_type::is_const,
             helper_type::is_static,
             &internal::meta_info<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, typename helper_type::return_type>>::resolve,
             &helper_type::arg,
             [](meta_handle instance, meta_any *args) {
-                return internal::invoke<Type, Candidate, Policy>(std::move(instance), args, std::make_index_sequence<std::tuple_size_v<typename helper_type::args_type>>{});
+                return internal::invoke<Type, Candidate, Policy>(std::move(instance), args, std::make_index_sequence<helper_type::args_type::size>{});
             }
         };
 

+ 49 - 45
src/entt/meta/meta.hpp

@@ -330,7 +330,7 @@ public:
     [[nodiscard]] const Type * try_cast() const {
         if(node) {
             if(const auto info = internal::meta_info<Type>::resolve()->info; node->info == info) {
-                return static_cast<const Type *>(storage.data());
+                return any_cast<Type>(&storage);
             } else if(const auto *base = internal::find_if<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type()->info == info; }, node); base) {
                 return static_cast<const Type *>(base->cast(storage.data()));
             }
@@ -344,12 +344,12 @@ public:
     [[nodiscard]] Type * try_cast() {
         if(node) {
             if(const auto info = internal::meta_info<Type>::resolve()->info; node->info == info) {
-                return static_cast<Type *>(storage.data());
+                return any_cast<Type>(&storage);
             } else if(const auto *base = internal::find_if<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type()->info == info; }, node); base) {
-                return static_cast<Type *>(const_cast<void *>(base->cast(storage.data())));
+                return static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(base->cast(static_cast<constness_as_t<any, Type> &>(storage).data())));
             }
         }
-
+        
         return nullptr;
     }
 
@@ -367,7 +367,7 @@ public:
      */
     template<typename Type>
     [[nodiscard]] Type cast() const {
-        auto * const actual = try_cast<std::remove_cv_t<std::remove_reference_t<Type>>>();
+        auto * const actual = try_cast<std::remove_reference_t<Type>>();
         ENTT_ASSERT(actual);
         return static_cast<Type>(*actual);
     }
@@ -375,30 +375,26 @@ public:
     /*! @copydoc cast */
     template<typename Type>
     [[nodiscard]] Type cast() {
-        if constexpr(!std::is_reference_v<Type> || std::is_const_v<std::remove_reference_t<Type>>) {
-            // last attempt to make wrappers for const references return their values
-            auto * const actual = std::as_const(*this).try_cast<std::remove_cv_t<std::remove_reference_t<Type>>>();
-            ENTT_ASSERT(actual);
-            return static_cast<Type>(*actual);
-        } else {
-            auto * const actual = try_cast<std::remove_cv_t<std::remove_reference_t<Type>>>();
-            ENTT_ASSERT(actual);
-            return static_cast<Type>(*actual);
-        }
+        // forces const on non-reference types to make them work also with wrappers for const references
+        auto * const actual = try_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>();
+        ENTT_ASSERT(actual);
+        return static_cast<Type>(*actual);
     }
 
     /**
-     * @brief Tries to convert an instance to a given type and returns it.
-     * @tparam Type Type to which to convert the instance.
-     * @return A valid meta any object if the conversion is possible, an invalid
-     * one otherwise.
+     * @brief Tries to make an instance castable to a certain type.
+     * @tparam Type Type to which the cast is requested.
+     * @return A valid meta any object if there exists a a viable conversion
+     * that makes the cast possible, an invalid object otherwise.
      */
     template<typename Type>
-    [[nodiscard]] meta_any convert() const {
-        if(node) {
-            if(const auto info = internal::meta_info<Type>::resolve()->info; node->info == info) {
-                return *this;
-            } else if(const auto * const conv = internal::find_if<&internal::meta_type_node::conv>([info](const auto *curr) { return curr->type()->info == info; }, node); conv) {
+    [[nodiscard]] meta_any allow_cast() const {
+        if(try_cast<std::remove_reference_t<Type>>() != nullptr) {
+            return as_ref(*this);
+        } else if(node) {
+            if(const auto * const conv = internal::find_if<&internal::meta_type_node::conv>([info = internal::meta_info<Type>::resolve()->info](const auto *curr) {
+                return curr->type()->info == info;
+            }, node); conv) {
                 return conv->conv(storage.data());
             }
         }
@@ -407,22 +403,25 @@ public:
     }
 
     /**
-     * @brief Tries to convert an instance to a given type.
-     * @tparam Type Type to which to convert the instance.
-     * @return True if the conversion is possible, false otherwise.
+     * @brief Tries to make an instance castable to a certain type.
+     * @tparam Type Type to which the cast is requested.
+     * @return True if there exists a a viable conversion that makes the cast
+     * possible, false otherwise.
      */
     template<typename Type>
-    bool convert() {
-        bool valid = (node && node->info == internal::meta_info<Type>::resolve()->info);
-
-        if(!valid) {
-            if(auto any = std::as_const(*this).convert<Type>(); any) {
-                swap(any, *this);
-                valid = true;
+    bool allow_cast() {
+        if(try_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>() != nullptr) {
+            return true;
+        } else if(node) {
+            if(const auto * const conv = internal::find_if<&internal::meta_type_node::conv>([info = internal::meta_info<Type>::resolve()->info](const auto *curr) {
+                return curr->type()->info == info;
+            }, node); conv) {
+                swap(conv->conv(std::as_const(storage).data()), *this);
+                return true;
             }
         }
 
-        return valid;
+        return false;
     }
 
     /**
@@ -570,6 +569,11 @@ struct meta_handle {
         return &any;
     }
 
+    /*! @brief operator-> */
+    [[nodiscard]] const meta_any * operator->() const {
+        return &any;
+    }
+
 private:
     meta_any any;
 };
@@ -1767,8 +1771,8 @@ struct meta_sequence_container::meta_sequence_container_proxy {
     }
 
     [[nodiscard]] static std::pair<iterator, bool> insert(void *container, iterator it, meta_any value) {
-        if(const auto *v_ptr = value.try_cast<typename traits_type::value_type>(); v_ptr || value.convert<typename traits_type::value_type>()) {
-            auto ret = traits_type::insert(*static_cast<Type *>(container), it.handle.cast<const typename traits_type::iterator &>(), v_ptr ? *v_ptr : value.cast<const typename traits_type::value_type &>());
+        if(value.allow_cast<const typename traits_type::value_type &>()) {
+            auto ret = traits_type::insert(*static_cast<Type *>(container), it.handle.cast<const typename traits_type::iterator &>(), value.cast<const typename traits_type::value_type &>());
             return { iterator{std::move(ret.first)}, ret.second };
         }
 
@@ -2034,12 +2038,12 @@ struct meta_associative_container::meta_associative_container_proxy {
     }
 
     [[nodiscard]] static bool insert(void *container, meta_any key, meta_any value) {
-        if(const auto *k_ptr = key.try_cast<typename traits_type::key_type>(); k_ptr || key.convert<typename traits_type::key_type>()) {
+        if(key.allow_cast<const typename traits_type::key_type &>()) {
             if constexpr(is_key_only_meta_associative_container_v<Type>) {
-                return traits_type::insert(*static_cast<Type *>(container), k_ptr ? *k_ptr : key.cast<const typename traits_type::key_type &>());
+                return traits_type::insert(*static_cast<Type *>(container), key.cast<const typename traits_type::key_type &>());
             } else {
-                if(const auto *m_ptr = value.try_cast<typename traits_type::mapped_type>(); m_ptr || value.convert<typename traits_type::mapped_type>()) {
-                    return traits_type::insert(*static_cast<Type *>(container), k_ptr ? *k_ptr : key.cast<const typename traits_type::key_type &>(), m_ptr ? *m_ptr : value.cast<const typename traits_type::mapped_type &>());
+                if(value.allow_cast<const typename traits_type::mapped_type &>()) {
+                    return traits_type::insert(*static_cast<Type *>(container), key.cast<const typename traits_type::key_type &>(), value.cast<const typename traits_type::mapped_type &>());
                 }
             }
         }
@@ -2048,16 +2052,16 @@ struct meta_associative_container::meta_associative_container_proxy {
     }
 
     [[nodiscard]] static bool erase(void *container, meta_any key) {
-        if(const auto *k_ptr = key.try_cast<typename traits_type::key_type>(); k_ptr || key.convert<typename traits_type::key_type>()) {
-            return traits_type::erase(*static_cast<Type *>(container), k_ptr ? *k_ptr : key.cast<const typename traits_type::key_type &>());
+        if(key.allow_cast<const typename traits_type::key_type &>()) {
+            return traits_type::erase(*static_cast<Type *>(container), key.cast<const typename traits_type::key_type &>());
         }
 
         return false;
     }
 
     [[nodiscard]] static iterator find(void *container, meta_any key) {
-        if(const auto *k_ptr = key.try_cast<typename traits_type::key_type>(); k_ptr || key.convert<typename traits_type::key_type>()) {
-            return iterator{is_key_only_meta_associative_container<Type>{}, traits_type::find(*static_cast<Type *>(container), k_ptr ? *k_ptr : key.cast<const typename traits_type::key_type &>())};
+        if(key.allow_cast<const typename traits_type::key_type &>()) {
+            return iterator{is_key_only_meta_associative_container<Type>{}, traits_type::find(*static_cast<Type *>(container), key.cast<const typename traits_type::key_type &>())};
         }
 
         return {};

+ 4 - 0
src/entt/meta/policy.hpp

@@ -9,6 +9,10 @@ namespace entt {
 struct as_ref_t {};
 
 
+/*! @brief Empty class type used to request the _as cref_ policy. */
+struct as_cref_t {};
+
+
 /*! @brief Empty class type used to request the _as-is_ policy. */
 struct as_is_t {};
 

+ 10 - 10
test/entt/meta/meta_any.cpp

@@ -596,11 +596,11 @@ TEST_F(MetaAny, Convert) {
 
     ASSERT_TRUE(any);
     ASSERT_EQ(any.type(), entt::resolve<double>());
-    ASSERT_TRUE(any.convert<double>());
-    ASSERT_FALSE(any.convert<char>());
+    ASSERT_TRUE(any.allow_cast<double>());
+    ASSERT_FALSE(any.allow_cast<char>());
     ASSERT_EQ(any.type(), entt::resolve<double>());
     ASSERT_EQ(any.cast<double>(), 42.);
-    ASSERT_TRUE(any.convert<int>());
+    ASSERT_TRUE(any.allow_cast<int>());
     ASSERT_EQ(any.type(), entt::resolve<int>());
     ASSERT_EQ(any.cast<int>(), 42);
 }
@@ -610,12 +610,12 @@ TEST_F(MetaAny, ConstConvert) {
 
     ASSERT_TRUE(any);
     ASSERT_EQ(any.type(), entt::resolve<double>());
-    ASSERT_TRUE(any.convert<double>());
-    ASSERT_FALSE(any.convert<char>());
+    ASSERT_TRUE(any.allow_cast<double>());
+    ASSERT_FALSE(any.allow_cast<char>());
     ASSERT_EQ(any.type(), entt::resolve<double>());
     ASSERT_EQ(any.cast<double>(), 42.);
 
-    auto other = any.convert<int>();
+    auto other = any.allow_cast<int>();
 
     ASSERT_EQ(any.type(), entt::resolve<double>());
     ASSERT_EQ(any.cast<double>(), 42.);
@@ -638,11 +638,11 @@ TEST_F(MetaAny, UnmanageableType) {
     ASSERT_EQ(any.try_cast<int>(), nullptr);
     ASSERT_NE(any.try_cast<unmanageable_t>(), nullptr);
 
-    ASSERT_TRUE(any.convert<unmanageable_t>());
-    ASSERT_FALSE(any.convert<int>());
+    ASSERT_TRUE(any.allow_cast<unmanageable_t>());
+    ASSERT_FALSE(any.allow_cast<int>());
 
-    ASSERT_TRUE(std::as_const(any).convert<unmanageable_t>());
-    ASSERT_FALSE(std::as_const(any).convert<int>());
+    ASSERT_TRUE(std::as_const(any).allow_cast<unmanageable_t>());
+    ASSERT_FALSE(std::as_const(any).allow_cast<int>());
 }
 
 TEST_F(MetaAny, Invoke) {

+ 2 - 2
test/entt/meta/meta_type.cpp

@@ -583,8 +583,8 @@ TEST_F(MetaType, ResetAndReRegistrationAfterReset) {
     entt::meta_any any{42.};
 
     ASSERT_TRUE(any);
-    ASSERT_FALSE(any.convert<int>());
-    ASSERT_TRUE(any.convert<float>());
+    ASSERT_FALSE(any.allow_cast<int>());
+    ASSERT_TRUE(any.allow_cast<float>());
 
     ASSERT_FALSE(entt::resolve("derived"_hs));
     ASSERT_TRUE(entt::resolve("double"_hs));

+ 1 - 1
test/lib/meta/main.cpp

@@ -35,7 +35,7 @@ TEST(Lib, Meta) {
     ASSERT_EQ(pos.type().data("y"_hs).get(pos).cast<int>(), 3);
 
     ASSERT_EQ(vel.type().data("dx"_hs).type(), entt::resolve<double>());
-    ASSERT_TRUE(vel.type().data("dy"_hs).get(vel).convert<double>());
+    ASSERT_TRUE(vel.type().data("dy"_hs).get(vel).allow_cast<double>());
     ASSERT_EQ(vel.type().data("dx"_hs).get(vel).cast<double>(), 0.);
     ASSERT_EQ(vel.type().data("dy"_hs).get(vel).cast<double>(), 0.);
 

+ 1 - 1
test/lib/meta_plugin/main.cpp

@@ -36,7 +36,7 @@ TEST(Lib, Meta) {
     ASSERT_EQ(pos.type().data("y"_hs).get(pos).cast<int>(), 3);
 
     ASSERT_EQ(vel.type().data("dx"_hs).type(), entt::resolve<double>());
-    ASSERT_TRUE(vel.type().data("dy"_hs).get(vel).convert<double>());
+    ASSERT_TRUE(vel.type().data("dy"_hs).get(vel).allow_cast<double>());
     ASSERT_EQ(vel.type().data("dx"_hs).get(vel).cast<double>(), 0.);
     ASSERT_EQ(vel.type().data("dy"_hs).get(vel).cast<double>(), 0.);
 

+ 1 - 1
test/lib/meta_plugin_std/main.cpp

@@ -36,7 +36,7 @@ TEST(Lib, Meta) {
     ASSERT_EQ(pos.type().data("y"_hs).get(pos).cast<int>(), 3);
 
     ASSERT_EQ(vel.type().data("dx"_hs).type(), entt::resolve<double>());
-    ASSERT_TRUE(vel.type().data("dy"_hs).get(vel).convert<double>());
+    ASSERT_TRUE(vel.type().data("dy"_hs).get(vel).allow_cast<double>());
     ASSERT_EQ(vel.type().data("dx"_hs).get(vel).cast<double>(), 0.);
     ASSERT_EQ(vel.type().data("dy"_hs).get(vel).cast<double>(), 0.);