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

meta system:
* perf improvements
* meta_any::try_cast
* meta_any is void-friendly
* meta_any support for unmanaged objects
* general cleanup

Michele Caini 6 лет назад
Родитель
Сommit
bad97a8b5b
5 измененных файлов с 640 добавлено и 593 удалено
  1. 1 1
      TODO
  2. 14 6
      docs/md/meta.md
  3. 282 87
      src/entt/meta/factory.hpp
  4. 146 373
      src/entt/meta/meta.hpp
  5. 197 126
      test/entt/meta/meta.cpp

+ 1 - 1
TODO

@@ -21,7 +21,7 @@
 * multi component registry::remove and some others?
 * can I use opaque connection also for the emitter?
 * built-in support for dual (or N-) buffering
-* meta: opaque references and pointers
 
 TODO
 * custom (decoupled) pools ==> double buffering, shared components, multi-model
+* use direct access (pool-like) also for context variables

+ 14 - 6
docs/md/meta.md

@@ -195,13 +195,21 @@ entt::meta_any any{0};
 entt::meta_any empty{};
 ```
 
-It can be constructed or assigned by copy and move and it takes the burden of
-destroying the contained object when required.<br/>
+It takes the burden of destroying the contained instance when required.<br/>
+Moreover, it can be used as an opaque container for unmanaged objects if needed:
+
+```cpp
+int value;
+entt::meta_any any{std::in_place, value};
+```
+
+In this case, the contained instance is never destroyed and users must ensure
+that the lifetime of the object exceeds that of the container.
+
 A meta any object has a `type` member function that returns the meta type of the
-contained value, if any. The member functions `can_cast` and `can_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. Similarly, `cast` and `convert` do what they promise
-and return the expected value.
+contained value, if any. 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.
 
 ## Enjoy the runtime
 

+ 282 - 87
src/entt/meta/factory.hpp

@@ -2,8 +2,11 @@
 #define ENTT_META_FACTORY_HPP
 
 
+#include <tuple>
+#include <array>
+#include <cstddef>
 #include <utility>
-#include <algorithm>
+#include <functional>
 #include <type_traits>
 #include "../config/config.h"
 #include "meta.hpp"
@@ -12,16 +15,220 @@
 namespace entt {
 
 
-template<typename>
-class meta_factory;
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
 
 
-template<typename Type, typename... Property>
-meta_factory<Type> reflect(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT;
+namespace internal {
 
 
-template<typename Type>
-bool unregister() ENTT_NOEXCEPT;
+template<typename...>
+struct meta_function_helper;
+
+
+template<typename Ret, typename... Args>
+struct meta_function_helper<Ret(Args...)> {
+    using return_type = Ret;
+    using args_type = std::tuple<Args...>;
+
+    template<std::size_t Index>
+    using arg_type = std::decay_t<std::tuple_element_t<Index, args_type>>;
+
+    static constexpr auto size = sizeof...(Args);
+
+    static auto arg(typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT {
+        return std::array<meta_type_node *, sizeof...(Args)>{{meta_info<Args>::resolve()...}}[index];
+    }
+};
+
+
+template<typename Class, typename Ret, typename... Args, bool Const, bool Static>
+struct meta_function_helper<Class, Ret(Args...), std::bool_constant<Const>, std::bool_constant<Static>>: meta_function_helper<Ret(Args...)> {
+    using class_type = Class;
+    static constexpr auto is_const = Const;
+    static constexpr auto is_static = Static;
+};
+
+
+template<typename Ret, typename... Args, typename Class>
+constexpr meta_function_helper<Class, Ret(Args...), std::bool_constant<false>, std::bool_constant<false>>
+to_meta_function_helper(Ret(Class:: *)(Args...));
+
+
+template<typename Ret, typename... Args, typename Class>
+constexpr meta_function_helper<Class, Ret(Args...), std::bool_constant<true>, std::bool_constant<false>>
+to_meta_function_helper(Ret(Class:: *)(Args...) const);
+
+
+template<typename Ret, typename... Args>
+constexpr meta_function_helper<void, Ret(Args...), std::bool_constant<false>, std::bool_constant<true>>
+to_meta_function_helper(Ret(*)(Args...));
+
+
+template<auto Func>
+struct meta_function_helper<std::integral_constant<decltype(Func), Func>>: decltype(to_meta_function_helper(Func)) {};
+
+
+template<typename Type, typename... Args, std::size_t... Indexes>
+meta_any construct(meta_any * const args, std::index_sequence<Indexes...>) {
+    [[maybe_unused]] auto direct = std::make_tuple((args+Indexes)->try_cast<std::remove_cv_t<std::remove_reference_t<Args>>>()...);
+    meta_any any{};
+
+    if(((std::get<Indexes>(direct) || (args+Indexes)->convert<std::remove_cv_t<std::remove_reference_t<Args>>>()) && ...)) {
+        any = Type{(std::get<Indexes>(direct) ? *std::get<Indexes>(direct) : (args+Indexes)->cast<std::remove_cv_t<std::remove_reference_t<Args>>>())...};
+    }
+
+    return any;
+}
+
+
+template<bool Const, typename Type, auto Data>
+bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index, [[maybe_unused]] meta_any value) {
+    bool accepted = false;
+
+    if constexpr(!Const) {
+        if constexpr(std::is_function_v<std::remove_pointer_t<decltype(Data)>> || std::is_member_function_pointer_v<decltype(Data)>) {
+            using helper_type = meta_function_helper<std::integral_constant<decltype(Data), Data>>;
+            using data_type = std::decay_t<std::tuple_element_t<!std::is_member_function_pointer_v<decltype(Data)>, typename helper_type::args_type>>;
+            static_assert(std::is_invocable_v<decltype(Data), Type &, data_type>);
+            auto *direct = value.try_cast<data_type>();
+            auto *clazz = handle.data<Type>();
+
+            if(clazz && (direct || value.convert<data_type>())) {
+                std::invoke(Data, *clazz, direct ? *direct : value.cast<data_type>());
+                accepted = true;
+            }
+        } else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
+            using data_type = std::remove_cv_t<std::remove_reference_t<decltype(std::declval<Type>().*Data)>>;
+            static_assert(std::is_invocable_v<decltype(Data), Type>);
+            auto *clazz = handle.data<Type>();
+
+            if constexpr(std::is_array_v<data_type>) {
+                using underlying_type = std::remove_extent_t<data_type>;
+                auto *direct = value.try_cast<underlying_type>();
+                auto *idx = index.try_cast<std::size_t>();
+
+                if(clazz && idx && (direct || value.convert<underlying_type>())) {
+                    std::invoke(Data, clazz)[*idx] = direct ? *direct : value.cast<underlying_type>();
+                    accepted = true;
+                }
+            } else {
+                auto *direct = value.try_cast<data_type>();
+
+                if(clazz && (direct || value.convert<data_type>())) {
+                    std::invoke(Data, clazz) = (direct ? *direct : value.cast<data_type>());
+                    accepted = true;
+                }
+            }
+        } else {
+            static_assert(std::is_pointer_v<decltype(Data)>);
+            using data_type = std::remove_cv_t<std::remove_reference_t<decltype(*Data)>>;
+
+            if constexpr(std::is_array_v<data_type>) {
+                using underlying_type = std::remove_extent_t<data_type>;
+                auto *direct = value.try_cast<underlying_type>();
+                auto *idx = index.try_cast<std::size_t>();
+
+                if(idx && (direct || value.convert<underlying_type>())) {
+                    (*Data)[*idx] = (direct ? *direct : value.cast<underlying_type>());
+                    accepted = true;
+                }
+            } else {
+                auto *direct = value.try_cast<data_type>();
+
+                if(direct || value.convert<data_type>()) {
+                    *Data = (direct ? *direct : value.cast<data_type>());
+                    accepted = true;
+                }
+            }
+        }
+    }
+
+    return accepted;
+}
+
+
+template<typename Type, auto Data>
+meta_any getter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index) {
+    if constexpr(std::is_function_v<std::remove_pointer_t<decltype(Data)>> || std::is_member_function_pointer_v<decltype(Data)>) {
+       static_assert(std::is_invocable_v<decltype(Data), Type &>);
+        auto *clazz = handle.data<Type>();
+        return clazz ? std::invoke(Data, *clazz) : meta_any{};
+    } else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
+        using data_type = std::remove_cv_t<std::remove_reference_t<decltype(std::declval<Type>().*Data)>>;
+        static_assert(std::is_invocable_v<decltype(Data), Type *>);
+        auto *clazz = handle.data<Type>();
+
+        if constexpr(std::is_array_v<data_type>) {
+            auto *idx = index.try_cast<std::size_t>();
+            return (clazz && idx) ? std::invoke(Data, clazz)[*idx] : meta_any{};
+        } else {
+            return clazz ? std::invoke(Data, clazz) : meta_any{};
+        }
+    } else {
+        static_assert(std::is_pointer_v<decltype(Data)>);
+
+        if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) {
+            auto *idx = index.try_cast<std::size_t>();
+            return idx ? (*Data)[*idx] : meta_any{};
+        } else {
+            return *Data;
+        }
+    }
+}
+
+
+template<typename Type, auto Func, std::size_t... Indexes>
+std::enable_if_t<std::is_function_v<std::remove_pointer_t<decltype(Func)>>, meta_any>
+invoke(meta_handle, meta_any *args, std::index_sequence<Indexes...>) {
+    using helper_type = meta_function_helper<std::integral_constant<decltype(Func), Func>>;
+    [[maybe_unused]] auto direct = std::make_tuple((args+Indexes)->try_cast<typename helper_type::template arg_type<Indexes>>()...);
+    meta_any any{};
+
+    if(((std::get<Indexes>(direct) || (args+Indexes)->convert<typename helper_type::template arg_type<Indexes>>()) && ...)) {
+        if constexpr(std::is_void_v<typename helper_type::return_type>) {
+            std::invoke(Func, (std::get<Indexes>(direct) ? *std::get<Indexes>(direct) : (args+Indexes)->cast<typename helper_type::template arg_type<Indexes>>())...);
+            any.emplace<void>();
+        } else {
+            any = std::invoke(Func, (std::get<Indexes>(direct) ? *std::get<Indexes>(direct) : (args+Indexes)->cast<typename helper_type::template arg_type<Indexes>>())...);
+        }
+    }
+
+    return any;
+}
+
+
+template<typename Type, auto Member, std::size_t... Indexes>
+std::enable_if_t<std::is_member_function_pointer_v<decltype(Member)>, meta_any>
+invoke(meta_handle handle, meta_any *args, std::index_sequence<Indexes...>) {
+    using helper_type = meta_function_helper<std::integral_constant<decltype(Member), Member>>;
+    static_assert(std::is_base_of_v<typename helper_type::class_type, Type>);
+    [[maybe_unused]] auto direct = std::make_tuple((args+Indexes)->try_cast<typename helper_type::template arg_type<Indexes>>()...);
+    auto *clazz = handle.data<Type>();
+    meta_any any{};
+
+    if(clazz && ((std::get<Indexes>(direct) || (args+Indexes)->convert<typename helper_type::template arg_type<Indexes>>()) && ...)) {
+        if constexpr(std::is_void_v<typename helper_type::return_type>) {
+            std::invoke(Member, clazz, (std::get<Indexes>(direct) ? *std::get<Indexes>(direct) : (args+Indexes)->cast<typename helper_type::template arg_type<Indexes>>())...);
+            any.emplace<void>();
+        } else {
+            any = std::invoke(Member, clazz, (std::get<Indexes>(direct) ? *std::get<Indexes>(direct) : (args+Indexes)->cast<typename helper_type::template arg_type<Indexes>>())...);
+        }
+    }
+
+    return any;
+}
+
+
+}
+
+
+/**
+ * Internal details not to be documented.
+ * @endcond TURN_OFF_DOXYGEN
+ */
 
 
 /**
@@ -75,44 +282,6 @@ class meta_factory {
         return &node;
     }
 
-    template<typename... Property>
-    meta_factory type(const ENTT_ID_TYPE identifier, 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>,
-            []() ENTT_NOEXCEPT -> meta_type {
-                return internal::meta_info<std::remove_pointer_t<Type>>::resolve();
-            },
-            &internal::destroy<Type>,
-            []() ENTT_NOEXCEPT -> meta_type {
-                return &node;
-            }
-        };
-
-        node.identifier = identifier;
-        node.next = internal::meta_info<>::type;
-        node.prop = properties<Type>(std::forward<Property>(property)...);
-        ENTT_ASSERT(!duplicate(identifier, 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;
@@ -150,46 +319,29 @@ class meta_factory {
         }
     }
 
-    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->identifier = {};
-            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 ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT;
+    /**
+     * @brief Extends a meta type by assigning it an identifier and properties.
+     * @tparam Property Types of properties to assign to the meta type.
+     * @param identifier Unique identifier.
+     * @param property Properties to assign to the meta type.
+     * @return A meta factory for the parent type.
+     */
+    template<typename... Property>
+    meta_factory type(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT {
+        ENTT_ASSERT(!internal::meta_info<Type>::type);
+        auto *node = internal::meta_info<Type>::resolve();
+        node->identifier = identifier;
+        node->next = internal::meta_info<>::type;
+        node->prop = properties<Type>(std::forward<Property>(property)...);
+        ENTT_ASSERT(!duplicate(identifier, node->next));
+        internal::meta_info<Type>::type = node;
+        internal::meta_info<>::type = node;
 
-    template<typename Other>
-    friend bool unregister() ENTT_NOEXCEPT;
+        return *this;
+    }
 
     /**
      * @brief Assigns a meta base to a meta type.
@@ -326,7 +478,7 @@ public:
             helper_type::size,
             &helper_type::arg,
             [](meta_any * const any) {
-                return internal::invoke<Type, Func>(nullptr, any, std::make_index_sequence<helper_type::size>{});
+                return internal::invoke<Type, Func>({}, any, std::make_index_sequence<helper_type::size>{});
             },
             []() ENTT_NOEXCEPT -> meta_ctor {
                 return &node;
@@ -409,7 +561,7 @@ public:
             type,
             [](meta_handle handle) {
                 return handle.type() == internal::meta_info<Type>::resolve()->meta()
-                        ? ((*Func)(static_cast<Type *>(handle.data())), true)
+                        ? ((*Func)(handle.data<Type>()), true)
                         : false;
             },
             []() ENTT_NOEXCEPT -> meta_dtor {
@@ -622,11 +774,54 @@ public:
 
         return *this;
     }
+
+    /**
+     * @brief Unregisters a meta type and all its parts.
+     *
+     * This function unregisters a meta 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.
+     *
+     * @return True if the meta type exists, false otherwise.
+     */
+    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->identifier = {};
+            internal::meta_info<Type>::type->next = nullptr;
+            internal::meta_info<Type>::type = nullptr;
+        }
+
+        return registered;
+    }
 };
 
 
 /**
- * @brief Basic function to use for reflection.
+ * @brief Utility 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
@@ -646,7 +841,7 @@ inline meta_factory<Type> reflect(const ENTT_ID_TYPE identifier, Property &&...
 
 
 /**
- * @brief Basic function to use for reflection.
+ * @brief Utility 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
@@ -663,7 +858,7 @@ inline meta_factory<Type> reflect() ENTT_NOEXCEPT {
 
 
 /**
- * @brief Basic function to unregister a type.
+ * @brief Utility 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
@@ -676,7 +871,7 @@ inline meta_factory<Type> reflect() ENTT_NOEXCEPT {
  */
 template<typename Type>
 inline bool unregister() ENTT_NOEXCEPT {
-    return meta_factory<Type>().unregister();
+    return meta_factory<Type>{}.unregister();
 }
 
 

+ 146 - 373
src/entt/meta/meta.hpp

@@ -2,13 +2,11 @@
 #define ENTT_META_META_HPP
 
 
-#include <tuple>
 #include <array>
 #include <memory>
 #include <cstring>
 #include <cstddef>
 #include <utility>
-#include <functional>
 #include <type_traits>
 #include "../config/config.h"
 
@@ -17,7 +15,7 @@ namespace entt {
 
 
 class meta_any;
-class meta_handle;
+struct meta_handle;
 class meta_prop;
 class meta_base;
 class meta_conv;
@@ -300,74 +298,41 @@ inline auto ctor(std::index_sequence<Indexes...>, const meta_type_node *node) EN
  */
 class meta_any {
     /*! @brief A meta handle is allowed to _inherit_ from a meta any. */
-    friend class meta_handle;
+    friend struct meta_handle;
 
     using storage_type = std::aligned_storage_t<sizeof(void *), alignof(void *)>;
     using compare_fn_type = bool(const void *, const void *);
     using copy_fn_type = void *(storage_type &, const void *);
     using destroy_fn_type = void(storage_type &);
-    using steal_fn_type = void *(storage_type &, storage_type &, destroy_fn_type *, void *) ENTT_NOEXCEPT;
+    using steal_fn_type = void *(storage_type &, storage_type &, destroy_fn_type *) ENTT_NOEXCEPT;
 
     template<typename Type, typename = std::void_t<>>
     struct type_traits {
-        using chunk_type = std::aligned_storage_t<sizeof(Type), alignof(Type)>;
-
         template<typename... Args>
         static void * instance(storage_type &storage, Args &&... args) {
-            auto chunk = std::make_unique<chunk_type>();
-            auto *instance = new (chunk.get()) Type{std::forward<Args>(args)...};
-            new (&storage) chunk_type *{chunk.get()};
-            chunk.release();
-            return instance;
+            auto instance = std::make_unique<Type>(std::forward<Args>(args)...);
+            new (&storage) Type *{instance.get()};
+            return instance.release();
         }
 
         static void destroy(storage_type &storage) {
             auto *node = internal::meta_info<Type>::resolve();
-            auto *chunk = *reinterpret_cast<chunk_type **>(&storage);
-            auto *instance = reinterpret_cast<Type *>(chunk);
+            auto *instance = *reinterpret_cast<Type **>(&storage);
             node->dtor ? node->dtor->invoke(*instance) : node->destroy(*instance);
-            delete chunk;
+            delete instance;
         }
 
-        static void * copy(storage_type &storage, const void *instance) {
-            auto chunk = std::make_unique<chunk_type>();
-            new (&storage) chunk_type *{chunk.get()};
-            auto *other = new (chunk.get()) Type{*static_cast<const Type *>(instance)};
-            chunk.release();
-            return other;
+        static void * copy(storage_type &storage, const void *other) {
+            auto instance = std::make_unique<Type>(*static_cast<const Type *>(other));
+            new (&storage) Type *{instance.get()};
+            return instance.release();
         }
 
-        static void * steal(storage_type &to, storage_type &from, destroy_fn_type *, void *instance) ENTT_NOEXCEPT {
-            auto *chunk = *reinterpret_cast<chunk_type **>(&from);
-            new (&to) chunk_type *{chunk};
-            chunk->~chunk_type();
+        static void * steal(storage_type &to, storage_type &from, destroy_fn_type *) ENTT_NOEXCEPT {
+            auto *instance = *reinterpret_cast<Type **>(&from);
+            new (&to) Type *{instance};
             return instance;
         }
-
-        static bool compare(const void *lhs, const void *rhs) {
-            return meta_any::compare(0, *static_cast<const Type *>(lhs), *static_cast<const Type *>(rhs));
-        }
-    };
-
-    template<typename Type>
-    struct type_traits<Type, std::enable_if_t<std::is_void_v<Type>>> {
-        static void * instance(storage_type &) {
-            return nullptr;
-        }
-
-        static void destroy(storage_type &) {}
-
-        static void * copy(storage_type &, const void *) {
-            return nullptr;
-        }
-
-        static void * steal(storage_type &, storage_type &, destroy_fn_type *, void *) ENTT_NOEXCEPT {
-            return nullptr;
-        }
-
-        static bool compare(const void *, const void *) {
-            return true;
-        }
     };
 
     template<typename Type>
@@ -381,25 +346,23 @@ class meta_any {
             auto *node = internal::meta_info<Type>::resolve();
             auto *instance = reinterpret_cast<Type *>(&storage);
             node->dtor ? node->dtor->invoke(*instance) : node->destroy(*instance);
+            instance->~Type();
         }
 
         static void * copy(storage_type &storage, const void *instance) {
             return new (&storage) Type{*static_cast<const Type *>(instance)};
         }
 
-        static void * steal(storage_type &to, storage_type &from, destroy_fn_type *destroy_fn, void *) ENTT_NOEXCEPT {
+        static void * steal(storage_type &to, storage_type &from, destroy_fn_type *destroy_fn) ENTT_NOEXCEPT {
             void *instance = new (&to) Type{std::move(*reinterpret_cast<Type *>(&from))};
             destroy_fn(from);
             return instance;
         }
-
-        static bool compare(const void *lhs, const void *rhs) {
-            return meta_any::compare(0, *static_cast<const Type *>(lhs), *static_cast<const Type *>(rhs));
-        }
     };
 
     template<typename Type>
-    static auto compare(int, const Type &lhs, const Type &rhs) -> decltype(lhs == rhs, bool{}) {
+    static auto compare(int, const Type &lhs, const Type &rhs)
+    -> decltype(lhs == rhs, bool{}) {
         return lhs == rhs;
     }
 
@@ -422,41 +385,48 @@ public:
 
     /**
      * @brief Constructs a meta any by directly initializing the new object.
-     *
-     * This class uses a technique called small buffer optimization (SBO) to
-     * completely eliminate the need to allocate memory, where possible.<br/>
-     * From the user's point of view, nothing will change, but the elimination
-     * of allocations will reduce the jumps in memory and therefore will avoid
-     * chasing of pointers. This will greatly improve the use of the cache, thus
-     * increasing the overall performance.
-     *
      * @tparam Type Type of object to use to initialize the container.
      * @tparam Args Types of arguments to use to construct the new instance.
      * @param args Parameters to use to construct the instance.
      */
     template<typename Type, typename... Args>
-    meta_any(std::in_place_type_t<Type>, Args &&... args) {
-        using actual_type = std::remove_cv_t<std::remove_reference_t<Type>>;
-        using traits_type = type_traits<actual_type>;
+    explicit meta_any(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
+        : meta_any{}
+    {
+        node = internal::meta_info<Type>::resolve();
+
+        if constexpr(!std::is_void_v<Type>) {
+            using traits_type = type_traits<std::remove_cv_t<std::remove_reference_t<Type>>>;
+            instance = traits_type::instance(storage, std::forward<Args>(args)...);
+            destroy_fn = &traits_type::destroy;
+            copy_fn = &traits_type::copy;
+            steal_fn = &traits_type::steal;
+
+            compare_fn = [](const void *lhs, const void *rhs) {
+                return compare(0, *static_cast<const Type *>(lhs), *static_cast<const Type *>(rhs));
+            };
+        }
+    }
 
+    /**
+     * @brief Constructs a meta any that holds an unmanaged object.
+     * @tparam Type Type of object to use to initialize the container.
+     * @param type An instance of an object to use to initialize the container.
+     */
+    template<typename Type>
+    explicit meta_any(std::in_place_t, Type &type)
+        : meta_any{}
+    {
         node = internal::meta_info<Type>::resolve();
-        instance = traits_type::instance(storage, std::forward<Args>(args)...);
-        destroy_fn = &traits_type::destroy;
-        copy_fn = &traits_type::copy;
-        steal_fn = &traits_type::steal;
-        compare_fn = &traits_type::compare;
+        instance = &type;
+
+        compare_fn = [](const void *lhs, const void *rhs) {
+            return compare(0, *static_cast<const Type *>(lhs), *static_cast<const Type *>(rhs));
+        };
     }
 
     /**
      * @brief Constructs a meta any from a given value.
-     *
-     * This class uses a technique called small buffer optimization (SBO) to
-     * completely eliminate the need to allocate memory, where possible.<br/>
-     * From the user's point of view, nothing will change, but the elimination
-     * of allocations will reduce the jumps in memory and therefore will avoid
-     * chasing of pointers. This will greatly improve the use of the cache, thus
-     * increasing the overall performance.
-     *
      * @tparam Type Type of object to use to initialize the container.
      * @param type An instance of an object to use to initialize the container.
      */
@@ -472,14 +442,12 @@ public:
     meta_any(const meta_any &other)
         : meta_any{}
     {
-        if(other) {
-            instance = other.copy_fn(storage, other.instance);
-            node = other.node;
-            destroy_fn = other.destroy_fn;
-            compare_fn = other.compare_fn;
-            copy_fn = other.copy_fn;
-            steal_fn = other.steal_fn;
-        }
+        node = other.node;
+        instance = other.copy_fn ? other.copy_fn(storage, other.instance) : other.instance;
+        destroy_fn = other.destroy_fn;
+        compare_fn = other.compare_fn;
+        copy_fn = other.copy_fn;
+        steal_fn = other.steal_fn;
     }
 
     /**
@@ -506,11 +474,32 @@ public:
 
     /**
      * @brief Assignment operator.
+     * @tparam Type Type of object to use to initialize the container.
+     * @param type An instance of an object to use to initialize the container.
+     * @return This meta any object.
+     */
+    template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_any>>>
+    meta_any & operator=(Type &&type) {
+        return (*this = meta_any{std::forward<Type>(type)});
+    }
+
+    /**
+     * @brief Copy assignment operator.
      * @param other The instance to assign.
      * @return This meta any object.
      */
-    meta_any & operator=(meta_any other) {
-        swap(other, *this);
+    meta_any & operator=(const meta_any &other) {
+        return (*this = meta_any{other});
+    }
+
+    /**
+     * @brief Move assignment operator.
+     * @param other The instance to assign.
+     * @return This meta any object.
+     */
+    meta_any & operator=(meta_any &&other) ENTT_NOEXCEPT {
+        meta_any any{std::move(other)};
+        swap(any, *this);
         return *this;
     }
 
@@ -534,14 +523,19 @@ public:
     }
 
     /**
-     * @brief Checks if it's possible to cast an instance to a given type.
+     * @brief Tries to cast an instance to a given type.
      * @tparam Type Type to which to cast the instance.
-     * @return True if the cast is viable, false otherwise.
+     * @return A (possibly null) pointer to the contained instance.
      */
     template<typename Type>
-    bool can_cast() const ENTT_NOEXCEPT {
-        const auto *type = internal::meta_info<Type>::resolve();
-        return internal::can_cast_or_convert<&internal::meta_type_node::base>(node, type);
+    const Type * try_cast() const ENTT_NOEXCEPT {
+        return internal::try_cast<Type>(node, instance);
+    }
+
+    /*! @copydoc try_cast */
+    template<typename Type>
+    Type * try_cast() ENTT_NOEXCEPT {
+        return const_cast<Type *>(std::as_const(*this).try_cast<Type>());
     }
 
     /**
@@ -560,8 +554,8 @@ public:
      */
     template<typename Type>
     const Type & cast() const ENTT_NOEXCEPT {
-        ENTT_ASSERT(can_cast<Type>());
-        return *internal::try_cast<Type>(node, instance);
+        ENTT_ASSERT(try_cast<Type>());
+        return *try_cast<Type>();
     }
 
     /*! @copydoc cast */
@@ -570,17 +564,6 @@ public:
         return const_cast<Type &>(std::as_const(*this).cast<Type>());
     }
 
-    /**
-     * @brief Checks if it's possible to convert an instance to a given type.
-     * @tparam Type Type to which to convert the instance.
-     * @return True if the conversion is viable, false otherwise.
-     */
-    template<typename Type>
-    bool can_convert() const ENTT_NOEXCEPT {
-        const auto *type = internal::meta_info<Type>::resolve();
-        return internal::can_cast_or_convert<&internal::meta_type_node::conv>(node, type);
-    }
-
     /**
      * @brief Tries to convert an instance to a given type and returns it.
      * @tparam Type Type to which to convert the instance.
@@ -642,7 +625,7 @@ public:
      * @return False if the container is empty, true otherwise.
      */
     explicit operator bool() const ENTT_NOEXCEPT {
-        return destroy_fn;
+        return node;
     }
 
     /**
@@ -652,7 +635,7 @@ public:
      * otherwise.
      */
     bool operator==(const meta_any &other) const ENTT_NOEXCEPT {
-        return (!compare_fn && !other.compare_fn) || (compare_fn && other.compare_fn && node == other.node && compare_fn(instance, other.instance));
+        return node == other.node && ((!compare_fn && !other.compare_fn) || compare_fn(instance, other.instance));
     }
 
     /**
@@ -661,17 +644,19 @@ public:
      * @param rhs A valid meta any object.
      */
     friend void swap(meta_any &lhs, meta_any &rhs) ENTT_NOEXCEPT {
-        if(lhs && rhs) {
+        if(lhs.steal_fn && rhs.steal_fn) {
             storage_type buffer;
-            void *instance = lhs.steal_fn(buffer, lhs.storage, lhs.destroy_fn, lhs.instance);
-            lhs.instance = rhs.steal_fn(lhs.storage, rhs.storage, rhs.destroy_fn, rhs.instance);
-            rhs.instance = lhs.steal_fn(rhs.storage, buffer, lhs.destroy_fn, instance);
-        } else if(lhs) {
-            rhs.instance = lhs.steal_fn(rhs.storage, lhs.storage, lhs.destroy_fn, lhs.instance);
-            lhs.instance = nullptr;
-        } else if(rhs) {
-            lhs.instance = rhs.steal_fn(lhs.storage, rhs.storage, rhs.destroy_fn, rhs.instance);
-            rhs.instance = nullptr;
+            lhs.steal_fn(buffer, lhs.storage, lhs.destroy_fn);
+            lhs.instance = rhs.steal_fn(lhs.storage, rhs.storage, rhs.destroy_fn);
+            rhs.instance = lhs.steal_fn(rhs.storage, buffer, lhs.destroy_fn);
+        } else if(lhs.steal_fn) {
+            lhs.instance = rhs.instance;
+            rhs.instance = lhs.steal_fn(rhs.storage, lhs.storage, lhs.destroy_fn);
+        } else if(rhs.steal_fn) {
+            rhs.instance = lhs.instance;
+            lhs.instance = rhs.steal_fn(lhs.storage, rhs.storage, rhs.destroy_fn);
+        } else {
+            std::swap(lhs.instance, rhs.instance);
         }
 
         std::swap(lhs.node, rhs.node);
@@ -702,33 +687,31 @@ private:
  * responsible for ensuring that the target object remains alive for the entire
  * interval of use of the handle.
  */
-class meta_handle {
-    meta_handle(int, meta_any &any) ENTT_NOEXCEPT
-        : node{any.node},
-          instance{any.instance}
-    {}
-
-    template<typename Type>
-    meta_handle(char, Type &&obj) ENTT_NOEXCEPT
-        : node{internal::meta_info<Type>::resolve()},
-          instance{&obj}
-    {}
-
-public:
+struct meta_handle {
     /*! @brief Default constructor. */
     meta_handle() ENTT_NOEXCEPT
         : node{nullptr},
           instance{nullptr}
     {}
 
+    /**
+     * @brief Constructs a meta handle from a meta any object.
+     * @param any A reference to an object to use to initialize the handle.
+     */
+    meta_handle(meta_any &any) ENTT_NOEXCEPT
+        : node{any.node},
+          instance{any.instance}
+    {}
+
     /**
      * @brief Constructs a meta handle from a given instance.
      * @tparam Type Type of object to use to initialize the handle.
      * @param obj A reference to an object to use to initialize the handle.
      */
     template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_handle>>>
-    meta_handle(Type &&obj) ENTT_NOEXCEPT
-        : meta_handle{0, std::forward<Type>(obj)}
+    meta_handle(Type &obj) ENTT_NOEXCEPT
+        : node{internal::meta_info<Type>::resolve()},
+          instance{&obj}
     {}
 
     /**
@@ -737,31 +720,6 @@ public:
      */
     inline meta_type type() const ENTT_NOEXCEPT;
 
-    /**
-     * @brief Tries to cast an instance to a given type.
-     *
-     * The type of the instance must be such that the conversion is possible.
-     *
-     * @warning
-     * Attempting to perform a conversion that isn't viable results in undefined
-     * behavior.<br/>
-     * An assertion will abort the execution at runtime in debug mode in case
-     * the conversion is not feasible.
-     *
-     * @tparam Type Type to which to cast the instance.
-     * @return A pointer to the contained instance.
-     */
-    template<typename Type>
-    const Type * try_cast() const ENTT_NOEXCEPT {
-        return internal::try_cast<Type>(node, instance);
-    }
-
-    /*! @copydoc try_cast */
-    template<typename Type>
-    Type * try_cast() ENTT_NOEXCEPT {
-        return const_cast<Type *>(std::as_const(*this).try_cast<Type>());
-    }
-
     /**
      * @brief Returns an opaque pointer to the contained instance.
      * @return An opaque pointer the contained instance, if any.
@@ -775,6 +733,22 @@ public:
         return const_cast<void *>(std::as_const(*this).data());
     }
 
+    /**
+     * @brief Tries to cast an instance to a given type.
+     * @tparam Type Type to which to cast the instance.
+     * @return A (possibly null) pointer to the underlying object.
+     */
+    template<typename Type>
+    const Type * data() const ENTT_NOEXCEPT {
+        return internal::try_cast<Type>(node, instance);
+    }
+
+    /*! @copydoc data */
+    template<typename Type>
+    Type * data() ENTT_NOEXCEPT {
+        return const_cast<Type *>(std::as_const(*this).data<Type>());
+    }
+
     /**
      * @brief Returns false if a handle is empty, true otherwise.
      * @return False if the handle is empty, true otherwise.
@@ -1590,9 +1564,6 @@ inline bool operator!=(const meta_func &lhs, const meta_func &rhs) ENTT_NOEXCEPT
  * able to work through it on real objects.
  */
 class meta_type {
-    /*! @brief A meta factory is allowed to create meta objects. */
-    template<typename> friend class meta_factory;
-
     /*! @brief A meta node is allowed to create meta objects. */
     template<typename...> friend struct internal::meta_node;
 
@@ -2093,215 +2064,6 @@ inline meta_type meta_func::arg(size_type index) const ENTT_NOEXCEPT {
 namespace internal {
 
 
-template<typename...>
-struct meta_function_helper;
-
-
-template<typename Ret, typename... Args>
-struct meta_function_helper<Ret(Args...)> {
-    using return_type = Ret;
-    using args_type = std::tuple<Args...>;
-
-    template<std::size_t Index>
-    using arg_type = std::decay_t<std::tuple_element_t<Index, args_type>>;
-
-    static constexpr auto size = sizeof...(Args);
-
-    static auto arg(typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT {
-        return std::array<meta_type_node *, sizeof...(Args)>{{meta_info<Args>::resolve()...}}[index];
-    }
-};
-
-
-template<typename Class, typename Ret, typename... Args, bool Const, bool Static>
-struct meta_function_helper<Class, Ret(Args...), std::bool_constant<Const>, std::bool_constant<Static>>: meta_function_helper<Ret(Args...)> {
-    using class_type = Class;
-    static constexpr auto is_const = Const;
-    static constexpr auto is_static = Static;
-};
-
-
-template<typename Ret, typename... Args, typename Class>
-constexpr meta_function_helper<Class, Ret(Args...), std::bool_constant<false>, std::bool_constant<false>>
-to_meta_function_helper(Ret(Class:: *)(Args...));
-
-
-template<typename Ret, typename... Args, typename Class>
-constexpr meta_function_helper<Class, Ret(Args...), std::bool_constant<true>, std::bool_constant<false>>
-to_meta_function_helper(Ret(Class:: *)(Args...) const);
-
-
-template<typename Ret, typename... Args>
-constexpr meta_function_helper<void, Ret(Args...), std::bool_constant<false>, std::bool_constant<true>>
-to_meta_function_helper(Ret(*)(Args...));
-
-
-template<auto Func>
-struct meta_function_helper<std::integral_constant<decltype(Func), Func>>: decltype(to_meta_function_helper(Func)) {};
-
-
-template<typename Type>
-bool destroy([[maybe_unused]] meta_handle handle) {
-    bool accepted = false;
-
-    if constexpr(std::is_object_v<Type> && !std::is_array_v<Type>) {
-        accepted = (handle.type() == meta_info<Type>::resolve()->meta());
-
-        if(accepted) {
-            static_cast<Type *>(handle.data())->~Type();
-        }
-    }
-
-    return accepted;
-}
-
-
-template<typename Type, typename... Args, std::size_t... Indexes>
-meta_any construct(meta_any * const args, std::index_sequence<Indexes...>) {
-    [[maybe_unused]] std::array<bool, sizeof...(Args)> can_cast{{(args+Indexes)->can_cast<std::remove_cv_t<std::remove_reference_t<Args>>>()...}};
-    [[maybe_unused]] std::array<bool, sizeof...(Args)> can_convert{{(std::get<Indexes>(can_cast) ? false : (args+Indexes)->can_convert<std::remove_cv_t<std::remove_reference_t<Args>>>())...}};
-    meta_any any{};
-
-    if(((std::get<Indexes>(can_cast) || std::get<Indexes>(can_convert)) && ...)) {
-        ((std::get<Indexes>(can_convert) ? void((args+Indexes)->convert<std::remove_cv_t<std::remove_reference_t<Args>>>()) : void()), ...);
-        any = Type{(args+Indexes)->cast<std::remove_cv_t<std::remove_reference_t<Args>>>()...};
-    }
-
-    return any;
-}
-
-
-template<bool Const, typename Type, auto Data>
-bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index, [[maybe_unused]] meta_any value) {
-    bool accepted = false;
-
-    if constexpr(!Const) {
-        if constexpr(std::is_function_v<std::remove_pointer_t<decltype(Data)>> || std::is_member_function_pointer_v<decltype(Data)>) {
-            using helper_type = meta_function_helper<std::integral_constant<decltype(Data), Data>>;
-            using data_type = std::decay_t<std::tuple_element_t<!std::is_member_function_pointer_v<decltype(Data)>, typename helper_type::args_type>>;
-            static_assert(std::is_invocable_v<decltype(Data), Type &, data_type>);
-            accepted = value.can_cast<data_type>() || value.convert<data_type>();
-            auto *clazz = handle.try_cast<Type>();
-
-            if(accepted && clazz) {
-                std::invoke(Data, *clazz, value.cast<data_type>());
-            }
-        } else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
-            using data_type = std::remove_cv_t<std::remove_reference_t<decltype(std::declval<Type>().*Data)>>;
-            static_assert(std::is_invocable_v<decltype(Data), Type>);
-            auto *clazz = handle.try_cast<Type>();
-
-            if constexpr(std::is_array_v<data_type>) {
-                using underlying_type = std::remove_extent_t<data_type>;
-                accepted = index.can_cast<std::size_t>() && (value.can_cast<underlying_type>() || value.convert<underlying_type>());
-
-                if(accepted && clazz) {
-                    std::invoke(Data, clazz)[index.cast<std::size_t>()] = value.cast<underlying_type>();
-                }
-            } else {
-                accepted = value.can_cast<data_type>() || value.convert<data_type>();
-
-                if(accepted && clazz) {
-                    std::invoke(Data, clazz) = value.cast<data_type>();
-                }
-            }
-        } else {
-            static_assert(std::is_pointer_v<decltype(Data)>);
-            using data_type = std::remove_cv_t<std::remove_reference_t<decltype(*Data)>>;
-
-            if constexpr(std::is_array_v<data_type>) {
-                using underlying_type = std::remove_extent_t<data_type>;
-                accepted = index.can_cast<std::size_t>() && (value.can_cast<underlying_type>() || value.convert<underlying_type>());
-
-                if(accepted) {
-                    (*Data)[index.cast<std::size_t>()] = value.cast<underlying_type>();
-                }
-            } else {
-                accepted = value.can_cast<data_type>() || value.convert<data_type>();
-
-                if(accepted) {
-                    *Data = value.cast<data_type>();
-                }
-            }
-        }
-    }
-
-    return accepted;
-}
-
-
-template<typename Type, auto Data>
-meta_any getter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index) {
-    if constexpr(std::is_function_v<std::remove_pointer_t<decltype(Data)>> || std::is_member_function_pointer_v<decltype(Data)>) {
-       static_assert(std::is_invocable_v<decltype(Data), Type &>);
-        auto *clazz = handle.try_cast<Type>();
-        return clazz ? std::invoke(Data, *clazz) : meta_any{};
-    } else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
-        using data_type = std::remove_cv_t<std::remove_reference_t<decltype(std::declval<Type>().*Data)>>;
-        static_assert(std::is_invocable_v<decltype(Data), Type *>);
-        auto *clazz = handle.try_cast<Type>();
-
-        if constexpr(std::is_array_v<data_type>) {
-            return (clazz && index.can_cast<std::size_t>()) ? std::invoke(Data, clazz)[index.cast<std::size_t>()] : meta_any{};
-        } else {
-            return clazz ? std::invoke(Data, clazz) : meta_any{};
-        }
-    } else {
-        static_assert(std::is_pointer_v<decltype(Data)>);
-
-        if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) {
-            return index.can_cast<std::size_t>() ? (*Data)[index.cast<std::size_t>()] : meta_any{};
-        } else {
-            return *Data;
-        }
-    }
-}
-
-
-template<typename Type, auto Func, std::size_t... Indexes>
-std::enable_if_t<std::is_function_v<std::remove_pointer_t<decltype(Func)>>, meta_any>
-invoke(const meta_handle &, meta_any *args, std::index_sequence<Indexes...>) {
-    using helper_type = meta_function_helper<std::integral_constant<decltype(Func), Func>>;
-    meta_any any{};
-
-    if((((args+Indexes)->can_cast<typename helper_type::template arg_type<Indexes>>()
-            || (args+Indexes)->convert<typename helper_type::template arg_type<Indexes>>()) && ...))
-    {
-        if constexpr(std::is_void_v<typename helper_type::return_type>) {
-            std::invoke(Func, (args+Indexes)->cast<typename helper_type::template arg_type<Indexes>>()...);
-            any.emplace<void>();
-        } else {
-            any = meta_any{std::invoke(Func, (args+Indexes)->cast<typename helper_type::template arg_type<Indexes>>()...)};
-        }
-    }
-
-    return any;
-}
-
-
-template<typename Type, auto Member, std::size_t... Indexes>
-std::enable_if_t<std::is_member_function_pointer_v<decltype(Member)>, meta_any>
-invoke(meta_handle &handle, meta_any *args, std::index_sequence<Indexes...>) {
-    using helper_type = meta_function_helper<std::integral_constant<decltype(Member), Member>>;
-    static_assert(std::is_base_of_v<typename helper_type::class_type, Type>);
-    auto *clazz = handle.try_cast<Type>();
-    meta_any any{};
-
-    if(clazz && (((args+Indexes)->can_cast<typename helper_type::template arg_type<Indexes>>()
-                  || (args+Indexes)->convert<typename helper_type::template arg_type<Indexes>>()) && ...))
-    {
-        if constexpr(std::is_void_v<typename helper_type::return_type>) {
-            std::invoke(Member, clazz, (args+Indexes)->cast<typename helper_type::template arg_type<Indexes>>()...);
-            any.emplace<void>();
-        } else {
-            any = meta_any{std::invoke(Member, clazz, (args+Indexes)->cast<typename helper_type::template arg_type<Indexes>>()...)};
-        }
-    }
-
-    return any;
-}
-
-
 template<typename Type>
 inline meta_type_node * meta_node<Type>::resolve() ENTT_NOEXCEPT {
     if(!type) {
@@ -2324,7 +2086,18 @@ inline meta_type_node * meta_node<Type>::resolve() ENTT_NOEXCEPT {
             []() ENTT_NOEXCEPT -> meta_type {
                 return internal::meta_info<std::remove_pointer_t<Type>>::resolve();
             },
-            &destroy<Type>,
+            []([[maybe_unused]] meta_handle handle) {
+                bool accepted = false;
+
+                if constexpr(std::is_object_v<Type> && !std::is_array_v<Type>) {
+                    if((handle.type() == meta_info<Type>::resolve()->meta())) {
+                        handle.data<Type>()->~Type();
+                        accepted = true;
+                    }
+                }
+
+                return accepted;
+            },
             []() ENTT_NOEXCEPT -> meta_type {
                 return &node;
             }

+ 197 - 126
test/entt/meta/meta.cpp

@@ -231,14 +231,14 @@ TEST_F(Meta, MetaAnySBO) {
     entt::meta_any any{'c'};
 
     ASSERT_TRUE(any);
-    ASSERT_FALSE(any.can_cast<void>());
-    ASSERT_TRUE(any.can_cast<char>());
+    ASSERT_FALSE(any.try_cast<std::size_t>());
+    ASSERT_TRUE(any.try_cast<char>());
     ASSERT_EQ(any.cast<char>(), 'c');
     ASSERT_EQ(std::as_const(any).cast<char>(), 'c');
     ASSERT_NE(any.data(), nullptr);
     ASSERT_NE(std::as_const(any).data(), nullptr);
     ASSERT_EQ(any, entt::meta_any{'c'});
-    ASSERT_NE(any, entt::meta_any{'h'});
+    ASSERT_NE(entt::meta_any{'h'}, any);
 }
 
 TEST_F(Meta, MetaAnyNoSBO) {
@@ -247,14 +247,14 @@ TEST_F(Meta, MetaAnyNoSBO) {
     entt::meta_any any{instance};
 
     ASSERT_TRUE(any);
-    ASSERT_FALSE(any.can_cast<void>());
-    ASSERT_TRUE(any.can_cast<fat_type>());
+    ASSERT_FALSE(any.try_cast<std::size_t>());
+    ASSERT_TRUE(any.try_cast<fat_type>());
     ASSERT_EQ(any.cast<fat_type>(), instance);
     ASSERT_EQ(std::as_const(any).cast<fat_type>(), instance);
     ASSERT_NE(any.data(), nullptr);
     ASSERT_NE(std::as_const(any).data(), nullptr);
     ASSERT_EQ(any, entt::meta_any{instance});
-    ASSERT_NE(any, fat_type{});
+    ASSERT_NE(fat_type{}, any);
 }
 
 TEST_F(Meta, MetaAnyEmpty) {
@@ -262,27 +262,45 @@ TEST_F(Meta, MetaAnyEmpty) {
 
     ASSERT_FALSE(any);
     ASSERT_FALSE(any.type());
-    ASSERT_FALSE(any.can_cast<void>());
-    ASSERT_FALSE(any.can_cast<empty_type>());
+    ASSERT_FALSE(any.try_cast<std::size_t>());
+    ASSERT_FALSE(any.try_cast<empty_type>());
     ASSERT_EQ(any.data(), nullptr);
     ASSERT_EQ(std::as_const(any).data(), nullptr);
     ASSERT_EQ(any, entt::meta_any{});
-    ASSERT_NE(any, entt::meta_any{'c'});
+    ASSERT_NE(entt::meta_any{'c'}, any);
 }
 
-TEST_F(Meta, MetaAnySBOInPlaceConstruction) {
+TEST_F(Meta, MetaAnySBOInPlaceTypeConstruction) {
     entt::meta_any any{std::in_place_type<int>, 42};
 
     ASSERT_TRUE(any);
-    ASSERT_FALSE(any.can_cast<void>());
-    ASSERT_TRUE(any.can_cast<int>());
+    ASSERT_FALSE(any.try_cast<std::size_t>());
+    ASSERT_TRUE(any.try_cast<int>());
     ASSERT_EQ(any.cast<int>(), 42);
     ASSERT_EQ(std::as_const(any).cast<int>(), 42);
     ASSERT_NE(any.data(), nullptr);
     ASSERT_NE(std::as_const(any).data(), nullptr);
     ASSERT_EQ(any, (entt::meta_any{std::in_place_type<int>, 42}));
     ASSERT_EQ(any, entt::meta_any{42});
-    ASSERT_NE(any, entt::meta_any{3});
+    ASSERT_NE(entt::meta_any{3}, any);
+}
+
+TEST_F(Meta, MetaAnySBOInPlaceConstruction) {
+    int value = 3;
+    int other = 42;
+    entt::meta_any any{std::in_place, value};
+
+    ASSERT_TRUE(any);
+    ASSERT_FALSE(any.try_cast<std::size_t>());
+    ASSERT_TRUE(any.try_cast<int>());
+    ASSERT_EQ(any.cast<int>(), 3);
+    ASSERT_EQ(std::as_const(any).cast<int>(), 3);
+    ASSERT_NE(any.data(), nullptr);
+    ASSERT_NE(std::as_const(any).data(), nullptr);
+    ASSERT_EQ(any, (entt::meta_any{std::in_place, value}));
+    ASSERT_NE(any, (entt::meta_any{std::in_place, other}));
+    ASSERT_NE(any, entt::meta_any{42});
+    ASSERT_EQ(entt::meta_any{3}, any);
 }
 
 TEST_F(Meta, MetaAnySBOCopyConstruction) {
@@ -291,8 +309,8 @@ TEST_F(Meta, MetaAnySBOCopyConstruction) {
 
     ASSERT_TRUE(any);
     ASSERT_TRUE(other);
-    ASSERT_FALSE(other.can_cast<void>());
-    ASSERT_TRUE(other.can_cast<int>());
+    ASSERT_FALSE(other.try_cast<std::size_t>());
+    ASSERT_TRUE(other.try_cast<int>());
     ASSERT_EQ(other.cast<int>(), 42);
     ASSERT_EQ(std::as_const(other).cast<int>(), 42);
     ASSERT_EQ(other, entt::meta_any{42});
@@ -307,8 +325,8 @@ TEST_F(Meta, MetaAnySBOCopyAssignment) {
 
     ASSERT_TRUE(any);
     ASSERT_TRUE(other);
-    ASSERT_FALSE(other.can_cast<void>());
-    ASSERT_TRUE(other.can_cast<int>());
+    ASSERT_FALSE(other.try_cast<std::size_t>());
+    ASSERT_TRUE(other.try_cast<int>());
     ASSERT_EQ(other.cast<int>(), 42);
     ASSERT_EQ(std::as_const(other).cast<int>(), 42);
     ASSERT_EQ(other, entt::meta_any{42});
@@ -321,8 +339,8 @@ TEST_F(Meta, MetaAnySBOMoveConstruction) {
 
     ASSERT_FALSE(any);
     ASSERT_TRUE(other);
-    ASSERT_FALSE(other.can_cast<void>());
-    ASSERT_TRUE(other.can_cast<int>());
+    ASSERT_FALSE(other.try_cast<std::size_t>());
+    ASSERT_TRUE(other.try_cast<int>());
     ASSERT_EQ(other.cast<int>(), 42);
     ASSERT_EQ(std::as_const(other).cast<int>(), 42);
     ASSERT_EQ(other, entt::meta_any{42});
@@ -337,29 +355,58 @@ TEST_F(Meta, MetaAnySBOMoveAssignment) {
 
     ASSERT_FALSE(any);
     ASSERT_TRUE(other);
-    ASSERT_FALSE(other.can_cast<void>());
-    ASSERT_TRUE(other.can_cast<int>());
+    ASSERT_FALSE(other.try_cast<std::size_t>());
+    ASSERT_TRUE(other.try_cast<int>());
     ASSERT_EQ(other.cast<int>(), 42);
     ASSERT_EQ(std::as_const(other).cast<int>(), 42);
     ASSERT_EQ(other, entt::meta_any{42});
     ASSERT_NE(other, entt::meta_any{0});
 }
 
-TEST_F(Meta, MetaAnyNoSBOInPlaceConstruction) {
+TEST_F(Meta, MetaAnySBODirectAssignment) {
+    entt::meta_any any{};
+    any = 42;
+
+    ASSERT_FALSE(any.try_cast<std::size_t>());
+    ASSERT_TRUE(any.try_cast<int>());
+    ASSERT_EQ(any.cast<int>(), 42);
+    ASSERT_EQ(std::as_const(any).cast<int>(), 42);
+    ASSERT_EQ(any, entt::meta_any{42});
+    ASSERT_NE(entt::meta_any{0}, any);
+}
+
+TEST_F(Meta, MetaAnyNoSBOInPlaceTypeConstruction) {
     int value = 42;
     fat_type instance{&value};
     entt::meta_any any{std::in_place_type<fat_type>, instance};
 
     ASSERT_TRUE(any);
-    ASSERT_FALSE(any.can_cast<void>());
-    ASSERT_TRUE(any.can_cast<fat_type>());
+    ASSERT_FALSE(any.try_cast<std::size_t>());
+    ASSERT_TRUE(any.try_cast<fat_type>());
     ASSERT_EQ(any.cast<fat_type>(), instance);
     ASSERT_EQ(std::as_const(any).cast<fat_type>(), instance);
     ASSERT_NE(any.data(), nullptr);
     ASSERT_NE(std::as_const(any).data(), nullptr);
     ASSERT_EQ(any, (entt::meta_any{std::in_place_type<fat_type>, instance}));
     ASSERT_EQ(any, entt::meta_any{instance});
-    ASSERT_NE(any, entt::meta_any{fat_type{}});
+    ASSERT_NE(entt::meta_any{fat_type{}}, any);
+}
+
+TEST_F(Meta, MetaAnyNoSBOInPlaceConstruction) {
+    int value = 3;
+    fat_type instance{&value};
+    entt::meta_any any{std::in_place, instance};
+
+    ASSERT_TRUE(any);
+    ASSERT_FALSE(any.try_cast<std::size_t>());
+    ASSERT_TRUE(any.try_cast<fat_type>());
+    ASSERT_EQ(any.cast<fat_type>(), instance);
+    ASSERT_EQ(std::as_const(any).cast<fat_type>(), instance);
+    ASSERT_NE(any.data(), nullptr);
+    ASSERT_NE(std::as_const(any).data(), nullptr);
+    ASSERT_EQ(any, (entt::meta_any{std::in_place, instance}));
+    ASSERT_EQ(any, entt::meta_any{instance});
+    ASSERT_NE(entt::meta_any{fat_type{}}, any);
 }
 
 TEST_F(Meta, MetaAnyNoSBOCopyConstruction) {
@@ -370,8 +417,8 @@ TEST_F(Meta, MetaAnyNoSBOCopyConstruction) {
 
     ASSERT_TRUE(any);
     ASSERT_TRUE(other);
-    ASSERT_FALSE(other.can_cast<void>());
-    ASSERT_TRUE(other.can_cast<fat_type>());
+    ASSERT_FALSE(other.try_cast<std::size_t>());
+    ASSERT_TRUE(other.try_cast<fat_type>());
     ASSERT_EQ(other.cast<fat_type>(), instance);
     ASSERT_EQ(std::as_const(other).cast<fat_type>(), instance);
     ASSERT_EQ(other, entt::meta_any{instance});
@@ -388,8 +435,8 @@ TEST_F(Meta, MetaAnyNoSBOCopyAssignment) {
 
     ASSERT_TRUE(any);
     ASSERT_TRUE(other);
-    ASSERT_FALSE(other.can_cast<void>());
-    ASSERT_TRUE(other.can_cast<fat_type>());
+    ASSERT_FALSE(other.try_cast<std::size_t>());
+    ASSERT_TRUE(other.try_cast<fat_type>());
     ASSERT_EQ(other.cast<fat_type>(), instance);
     ASSERT_EQ(std::as_const(other).cast<fat_type>(), instance);
     ASSERT_EQ(other, entt::meta_any{instance});
@@ -404,8 +451,8 @@ TEST_F(Meta, MetaAnyNoSBOMoveConstruction) {
 
     ASSERT_FALSE(any);
     ASSERT_TRUE(other);
-    ASSERT_FALSE(other.can_cast<void>());
-    ASSERT_TRUE(other.can_cast<fat_type>());
+    ASSERT_FALSE(other.try_cast<std::size_t>());
+    ASSERT_TRUE(other.try_cast<fat_type>());
     ASSERT_EQ(other.cast<fat_type>(), instance);
     ASSERT_EQ(std::as_const(other).cast<fat_type>(), instance);
     ASSERT_EQ(other, entt::meta_any{instance});
@@ -422,23 +469,37 @@ TEST_F(Meta, MetaAnyNoSBOMoveAssignment) {
 
     ASSERT_FALSE(any);
     ASSERT_TRUE(other);
-    ASSERT_FALSE(other.can_cast<void>());
-    ASSERT_TRUE(other.can_cast<fat_type>());
+    ASSERT_FALSE(other.try_cast<std::size_t>());
+    ASSERT_TRUE(other.try_cast<fat_type>());
     ASSERT_EQ(other.cast<fat_type>(), instance);
     ASSERT_EQ(std::as_const(other).cast<fat_type>(), instance);
     ASSERT_EQ(other, entt::meta_any{instance});
     ASSERT_NE(other, fat_type{});
 }
 
-TEST_F(Meta, MetaAnyVoidInPlaceConstruction) {
+TEST_F(Meta, MetaAnyNoSBODirectAssignment) {
+    int value = 42;
+    entt::meta_any any{};
+    any = fat_type{&value};
+
+    ASSERT_FALSE(any.try_cast<std::size_t>());
+    ASSERT_TRUE(any.try_cast<fat_type>());
+    ASSERT_EQ(any.cast<fat_type>(), fat_type{&value});
+    ASSERT_EQ(std::as_const(any).cast<fat_type>(), fat_type{&value});
+    ASSERT_EQ(any, entt::meta_any{fat_type{&value}});
+    ASSERT_NE(fat_type{}, any);
+}
+
+TEST_F(Meta, MetaAnyVoidInPlaceTypeConstruction) {
     entt::meta_any any{std::in_place_type<void>};
 
     ASSERT_TRUE(any);
-    ASSERT_TRUE(any.can_cast<void>());
-    ASSERT_FALSE(any.can_cast<char>());
+    ASSERT_FALSE(any.try_cast<char>());
     ASSERT_EQ(any.data(), nullptr);
     ASSERT_EQ(std::as_const(any).data(), nullptr);
+    ASSERT_EQ(any.type(), entt::resolve<void>());
     ASSERT_EQ(any, entt::meta_any{std::in_place_type<void>});
+    ASSERT_NE(entt::meta_any{3}, any);
 }
 
 TEST_F(Meta, MetaAnyVoidCopyConstruction) {
@@ -447,7 +508,7 @@ TEST_F(Meta, MetaAnyVoidCopyConstruction) {
 
     ASSERT_TRUE(any);
     ASSERT_TRUE(other);
-    ASSERT_TRUE(other.can_cast<void>());
+    ASSERT_EQ(any.type(), entt::resolve<void>());
     ASSERT_EQ(other, entt::meta_any{std::in_place_type<void>});
 }
 
@@ -459,7 +520,7 @@ TEST_F(Meta, MetaAnyVoidCopyAssignment) {
 
     ASSERT_TRUE(any);
     ASSERT_TRUE(other);
-    ASSERT_TRUE(other.can_cast<void>());
+    ASSERT_EQ(any.type(), entt::resolve<void>());
     ASSERT_EQ(other, entt::meta_any{std::in_place_type<void>});
 }
 
@@ -469,7 +530,7 @@ TEST_F(Meta, MetaAnyVoidMoveConstruction) {
 
     ASSERT_FALSE(any);
     ASSERT_TRUE(other);
-    ASSERT_TRUE(other.can_cast<void>());
+    ASSERT_EQ(other.type(), entt::resolve<void>());
     ASSERT_EQ(other, entt::meta_any{std::in_place_type<void>});
 }
 
@@ -481,7 +542,7 @@ TEST_F(Meta, MetaAnyVoidMoveAssignment) {
 
     ASSERT_FALSE(any);
     ASSERT_TRUE(other);
-    ASSERT_TRUE(other.can_cast<void>());
+    ASSERT_EQ(other.type(), entt::resolve<void>());
     ASSERT_EQ(other, entt::meta_any{std::in_place_type<void>});
 }
 
@@ -539,15 +600,15 @@ TEST_F(Meta, MetaAnyEmplace) {
     any.emplace<int>(42);
 
     ASSERT_TRUE(any);
-    ASSERT_FALSE(any.can_cast<void>());
-    ASSERT_TRUE(any.can_cast<int>());
+    ASSERT_FALSE(any.try_cast<std::size_t>());
+    ASSERT_TRUE(any.try_cast<int>());
     ASSERT_EQ(any.cast<int>(), 42);
     ASSERT_EQ(std::as_const(any).cast<int>(), 42);
     ASSERT_NE(any.data(), nullptr);
     ASSERT_NE(std::as_const(any).data(), nullptr);
     ASSERT_EQ(any, (entt::meta_any{std::in_place_type<int>, 42}));
     ASSERT_EQ(any, entt::meta_any{42});
-    ASSERT_NE(any, entt::meta_any{3});
+    ASSERT_NE(entt::meta_any{3}, any);
 }
 
 TEST_F(Meta, MetaAnyEmplaceVoid) {
@@ -555,9 +616,9 @@ TEST_F(Meta, MetaAnyEmplaceVoid) {
     any.emplace<void>();
 
     ASSERT_TRUE(any);
-    ASSERT_TRUE(any.can_cast<void>());
     ASSERT_EQ(any.data(), nullptr);
     ASSERT_EQ(std::as_const(any).data(), nullptr);
+    ASSERT_EQ(any.type(), entt::resolve<void>());
     ASSERT_EQ(any, (entt::meta_any{std::in_place_type<void>}));
 }
 
@@ -567,9 +628,9 @@ TEST_F(Meta, MetaAnySBOSwap) {
 
     std::swap(lhs, rhs);
 
-    ASSERT_TRUE(lhs.can_cast<int>());
+    ASSERT_TRUE(lhs.try_cast<int>());
     ASSERT_EQ(lhs.cast<int>(), 42);
-    ASSERT_TRUE(rhs.can_cast<char>());
+    ASSERT_TRUE(rhs.try_cast<char>());
     ASSERT_EQ(rhs.cast<char>(), 'c');
 }
 
@@ -601,9 +662,9 @@ TEST_F(Meta, MetaAnySBOWithNoSBOSwap) {
 
     std::swap(lhs, rhs);
 
-    ASSERT_TRUE(lhs.can_cast<char>());
+    ASSERT_TRUE(lhs.try_cast<char>());
     ASSERT_EQ(lhs.cast<char>(), 'c');
-    ASSERT_TRUE(rhs.can_cast<fat_type>());
+    ASSERT_TRUE(rhs.try_cast<fat_type>());
     ASSERT_EQ(rhs.cast<fat_type>().foo, &value);
     ASSERT_EQ(rhs.cast<fat_type>().bar, &value);
 }
@@ -615,13 +676,13 @@ TEST_F(Meta, MetaAnySBOWithEmptySwap) {
     std::swap(lhs, rhs);
 
     ASSERT_FALSE(lhs);
-    ASSERT_TRUE(rhs.can_cast<char>());
+    ASSERT_TRUE(rhs.try_cast<char>());
     ASSERT_EQ(rhs.cast<char>(), 'c');
 
     std::swap(lhs, rhs);
 
     ASSERT_FALSE(rhs);
-    ASSERT_TRUE(lhs.can_cast<char>());
+    ASSERT_TRUE(lhs.try_cast<char>());
     ASSERT_EQ(lhs.cast<char>(), 'c');
 }
 
@@ -631,8 +692,8 @@ TEST_F(Meta, MetaAnySBOWithVoidSwap) {
 
     std::swap(lhs, rhs);
 
-    ASSERT_TRUE(lhs.can_cast<void>());
-    ASSERT_TRUE(rhs.can_cast<char>());
+    ASSERT_EQ(lhs.type(), entt::resolve<void>());
+    ASSERT_TRUE(rhs.try_cast<char>());
     ASSERT_EQ(rhs.cast<char>(), 'c');
 }
 
@@ -669,7 +730,7 @@ TEST_F(Meta, MetaAnyComparable) {
 
     ASSERT_EQ(any, any);
     ASSERT_EQ(any, entt::meta_any{'c'});
-    ASSERT_NE(any, entt::meta_any{'a'});
+    ASSERT_NE(entt::meta_any{'a'}, any);
     ASSERT_NE(any, entt::meta_any{});
 
     ASSERT_TRUE(any == any);
@@ -684,7 +745,7 @@ TEST_F(Meta, MetaAnyNotComparable) {
 
     ASSERT_EQ(any, any);
     ASSERT_NE(any, entt::meta_any{not_comparable_type{}});
-    ASSERT_NE(any, entt::meta_any{});
+    ASSERT_NE(entt::meta_any{}, any);
 
     ASSERT_TRUE(any == any);
     ASSERT_FALSE(any == entt::meta_any{not_comparable_type{}});
@@ -696,7 +757,7 @@ TEST_F(Meta, MetaAnyCompareVoid) {
 
     ASSERT_EQ(any, any);
     ASSERT_EQ(any, entt::meta_any{std::in_place_type<void>});
-    ASSERT_NE(any, entt::meta_any{'a'});
+    ASSERT_NE(entt::meta_any{'a'}, any);
     ASSERT_NE(any, entt::meta_any{});
 
     ASSERT_TRUE(any == any);
@@ -706,19 +767,28 @@ TEST_F(Meta, MetaAnyCompareVoid) {
     ASSERT_TRUE(any != entt::meta_any{});
 }
 
+TEST_F(Meta, MetaAnyTryCast) {
+    entt::meta_any any{derived_type{}};
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.type(), entt::resolve<derived_type>());
+    ASSERT_EQ(any.try_cast<void>(), nullptr);
+    ASSERT_NE(any.try_cast<base_type>(), nullptr);
+    ASSERT_EQ(any.try_cast<derived_type>(), any.data());
+    ASSERT_EQ(std::as_const(any).try_cast<base_type>(), any.try_cast<base_type>());
+    ASSERT_EQ(std::as_const(any).try_cast<derived_type>(), any.data());
+}
+
 TEST_F(Meta, MetaAnyCast) {
     entt::meta_any any{derived_type{}};
-    entt::meta_handle handle{any};
 
     ASSERT_TRUE(any);
     ASSERT_EQ(any.type(), entt::resolve<derived_type>());
-    ASSERT_FALSE(any.can_cast<void>());
-    ASSERT_TRUE(any.can_cast<base_type>());
-    ASSERT_TRUE(any.can_cast<derived_type>());
-    ASSERT_EQ(&any.cast<base_type>(), handle.try_cast<base_type>());
-    ASSERT_EQ(&any.cast<derived_type>(), handle.try_cast<derived_type>());
-    ASSERT_EQ(&std::as_const(any).cast<base_type>(), handle.try_cast<base_type>());
-    ASSERT_EQ(&std::as_const(any).cast<derived_type>(), handle.try_cast<derived_type>());
+    ASSERT_EQ(any.try_cast<std::size_t>(), nullptr);
+    ASSERT_NE(any.try_cast<base_type>(), nullptr);
+    ASSERT_EQ(any.try_cast<derived_type>(), any.data());
+    ASSERT_EQ(std::as_const(any).try_cast<base_type>(), any.try_cast<base_type>());
+    ASSERT_EQ(std::as_const(any).try_cast<derived_type>(), any.data());
 }
 
 TEST_F(Meta, MetaAnyConvert) {
@@ -726,18 +796,11 @@ TEST_F(Meta, MetaAnyConvert) {
 
     ASSERT_TRUE(any);
     ASSERT_EQ(any.type(), entt::resolve<double>());
-    ASSERT_FALSE(any.can_convert<char>());
-    ASSERT_TRUE(any.can_convert<double>());
-    ASSERT_TRUE(any.can_convert<int>());
-
     ASSERT_TRUE(any.convert<double>());
     ASSERT_FALSE(any.convert<char>());
-
     ASSERT_EQ(any.type(), entt::resolve<double>());
     ASSERT_EQ(any.cast<double>(), 42.);
-
     ASSERT_TRUE(any.convert<int>());
-
     ASSERT_EQ(any.type(), entt::resolve<int>());
     ASSERT_EQ(any.cast<int>(), 42);
 }
@@ -747,13 +810,8 @@ TEST_F(Meta, MetaAnyConstConvert) {
 
     ASSERT_TRUE(any);
     ASSERT_EQ(any.type(), entt::resolve<double>());
-    ASSERT_FALSE(any.can_convert<char>());
-    ASSERT_TRUE(any.can_convert<double>());
-    ASSERT_TRUE(any.can_convert<int>());
-
     ASSERT_TRUE(any.convert<double>());
     ASSERT_FALSE(any.convert<char>());
-
     ASSERT_EQ(any.type(), entt::resolve<double>());
     ASSERT_EQ(any.cast<double>(), 42.);
 
@@ -771,9 +829,9 @@ TEST_F(Meta, MetaHandleFromObject) {
 
     ASSERT_TRUE(handle);
     ASSERT_EQ(handle.type(), entt::resolve<empty_type>());
-    ASSERT_EQ(handle.try_cast<void>(), nullptr);
-    ASSERT_EQ(handle.try_cast<empty_type>(), &empty);
-    ASSERT_EQ(std::as_const(handle).try_cast<empty_type>(), &empty);
+    ASSERT_EQ(handle.data<std::size_t>(), nullptr);
+    ASSERT_EQ(handle.data<empty_type>(), &empty);
+    ASSERT_EQ(std::as_const(handle).data<empty_type>(), &empty);
     ASSERT_EQ(handle.data(), &empty);
     ASSERT_EQ(std::as_const(handle).data(), &empty);
 }
@@ -784,9 +842,9 @@ TEST_F(Meta, MetaHandleFromMetaAny) {
 
     ASSERT_TRUE(handle);
     ASSERT_EQ(handle.type(), entt::resolve<int>());
-    ASSERT_EQ(handle.try_cast<void>(), nullptr);
-    ASSERT_EQ(handle.try_cast<int>(), any.data());
-    ASSERT_EQ(std::as_const(handle).try_cast<int>(), any.data());
+    ASSERT_EQ(handle.data<std::size_t>(), nullptr);
+    ASSERT_EQ(handle.data<int>(), any.data());
+    ASSERT_EQ(std::as_const(handle).data<int>(), any.data());
     ASSERT_EQ(handle.data(), any.data());
     ASSERT_EQ(std::as_const(handle).data(), any.data());
 }
@@ -796,24 +854,24 @@ TEST_F(Meta, MetaHandleEmpty) {
 
     ASSERT_FALSE(handle);
     ASSERT_FALSE(handle.type());
-    ASSERT_EQ(handle.try_cast<void>(), nullptr);
-    ASSERT_EQ(handle.try_cast<empty_type>(), nullptr);
+    ASSERT_EQ(handle.data<std::size_t>(), nullptr);
+    ASSERT_EQ(handle.data<empty_type>(), nullptr);
     ASSERT_EQ(handle.data(), nullptr);
     ASSERT_EQ(std::as_const(handle).data(), nullptr);
 }
 
-TEST_F(Meta, MetaHandleTryCast) {
+TEST_F(Meta, MetaHandleData) {
     derived_type derived{};
     base_type *base = &derived;
     entt::meta_handle handle{derived};
 
     ASSERT_TRUE(handle);
     ASSERT_EQ(handle.type(), entt::resolve<derived_type>());
-    ASSERT_EQ(handle.try_cast<void>(), nullptr);
-    ASSERT_EQ(handle.try_cast<base_type>(), base);
-    ASSERT_EQ(handle.try_cast<derived_type>(), &derived);
-    ASSERT_EQ(std::as_const(handle).try_cast<base_type>(), base);
-    ASSERT_EQ(std::as_const(handle).try_cast<derived_type>(), &derived);
+    ASSERT_EQ(handle.data<std::size_t>(), nullptr);
+    ASSERT_EQ(handle.data<base_type>(), base);
+    ASSERT_EQ(handle.data<derived_type>(), &derived);
+    ASSERT_EQ(std::as_const(handle).data<base_type>(), base);
+    ASSERT_EQ(std::as_const(handle).data<derived_type>(), &derived);
     ASSERT_EQ(handle.data(), &derived);
     ASSERT_EQ(std::as_const(handle).data(), &derived);
 }
@@ -903,7 +961,7 @@ TEST_F(Meta, MetaCtor) {
 
     ASSERT_FALSE(empty);
     ASSERT_TRUE(any);
-    ASSERT_TRUE(any.can_cast<derived_type>());
+    ASSERT_TRUE(any.try_cast<derived_type>());
     ASSERT_EQ(any.cast<derived_type>().i, 42);
     ASSERT_EQ(any.cast<derived_type>().c, 'c');
 
@@ -937,7 +995,7 @@ TEST_F(Meta, MetaCtorFunc) {
 
     ASSERT_FALSE(empty);
     ASSERT_TRUE(any);
-    ASSERT_TRUE(any.can_cast<derived_type>());
+    ASSERT_TRUE(any.try_cast<derived_type>());
     ASSERT_EQ(any.cast<derived_type>().i, 42);
     ASSERT_EQ(any.cast<derived_type>().c, 'c');
 
@@ -961,7 +1019,7 @@ TEST_F(Meta, MetaCtorMetaAnyArgs) {
     auto any = ctor.invoke(base_type{}, entt::meta_any{42}, entt::meta_any{'c'});
 
     ASSERT_TRUE(any);
-    ASSERT_TRUE(any.can_cast<derived_type>());
+    ASSERT_TRUE(any.try_cast<derived_type>());
     ASSERT_EQ(any.cast<derived_type>().i, 42);
     ASSERT_EQ(any.cast<derived_type>().c, 'c');
 }
@@ -976,7 +1034,7 @@ TEST_F(Meta, MetaCtorCastAndConvert) {
     auto any = ctor.invoke(entt::meta_any{derived_type{}}, entt::meta_any{42.}, entt::meta_any{'c'});
 
     ASSERT_TRUE(any);
-    ASSERT_TRUE(any.can_cast<derived_type>());
+    ASSERT_TRUE(any.try_cast<derived_type>());
     ASSERT_EQ(any.cast<derived_type>().i, 42);
     ASSERT_EQ(any.cast<derived_type>().c, 'c');
 }
@@ -986,7 +1044,7 @@ TEST_F(Meta, MetaCtorFuncMetaAnyArgs) {
     auto any = ctor.invoke(base_type{}, entt::meta_any{42});
 
     ASSERT_TRUE(any);
-    ASSERT_TRUE(any.can_cast<derived_type>());
+    ASSERT_TRUE(any.try_cast<derived_type>());
     ASSERT_EQ(any.cast<derived_type>().i, 42);
     ASSERT_EQ(any.cast<derived_type>().c, 'c');
 }
@@ -1001,7 +1059,7 @@ TEST_F(Meta, MetaCtorFuncCastAndConvert) {
     auto any = ctor.invoke(entt::meta_any{derived_type{}}, entt::meta_any{42.});
 
     ASSERT_TRUE(any);
-    ASSERT_TRUE(any.can_cast<derived_type>());
+    ASSERT_TRUE(any.try_cast<derived_type>());
     ASSERT_EQ(any.cast<derived_type>().i, 42);
     ASSERT_EQ(any.cast<derived_type>().c, 'c');
 }
@@ -1028,7 +1086,8 @@ TEST_F(Meta, MetaDtorMetaAnyArg) {
 }
 
 TEST_F(Meta, MetaDtorMetaAnyInvalidArg) {
-    ASSERT_FALSE(entt::resolve<empty_type>().dtor().invoke(int{}));
+    auto instance = 0;
+    ASSERT_FALSE(entt::resolve<empty_type>().dtor().invoke(instance));
 }
 
 
@@ -1154,12 +1213,13 @@ TEST_F(Meta, MetaDataGetMetaAnyArg) {
     const auto value = data.get(any);
 
     ASSERT_TRUE(value);
-    ASSERT_TRUE(value.can_cast<int>());
+    ASSERT_TRUE(value.cast<int>());
     ASSERT_EQ(value.cast<int>(), 99);
 }
 
 TEST_F(Meta, MetaDataGetInvalidArg) {
-    ASSERT_FALSE(entt::resolve<data_type>().data("i"_hs).get(0));
+    auto instance = 0;
+    ASSERT_FALSE(entt::resolve<data_type>().data("i"_hs).get(instance));
 }
 
 TEST_F(Meta, MetaDataSetMetaAnyArg) {
@@ -1274,17 +1334,17 @@ TEST_F(Meta, MetaDataArrayStatic) {
     ASSERT_TRUE(data.is_static());
     ASSERT_TRUE(data.type().is_array());
     ASSERT_EQ(data.type().extent(), 3);
-    ASSERT_EQ(data.get(nullptr, 0).cast<int>(), 3);
-    ASSERT_EQ(data.get(nullptr, 1).cast<int>(), 5);
-    ASSERT_EQ(data.get(nullptr, 2).cast<int>(), 7);
-    ASSERT_FALSE(data.set(nullptr, 0, 'c'));
-    ASSERT_EQ(data.get(nullptr, 0).cast<int>(), 3);
-    ASSERT_TRUE(data.set(nullptr, 0, data.get(nullptr, 0).cast<int>()+2));
-    ASSERT_TRUE(data.set(nullptr, 1, data.get(nullptr, 1).cast<int>()+2));
-    ASSERT_TRUE(data.set(nullptr, 2, data.get(nullptr, 2).cast<int>()+2));
-    ASSERT_EQ(data.get(nullptr, 0).cast<int>(), 5);
-    ASSERT_EQ(data.get(nullptr, 1).cast<int>(), 7);
-    ASSERT_EQ(data.get(nullptr, 2).cast<int>(), 9);
+    ASSERT_EQ(data.get({}, 0).cast<int>(), 3);
+    ASSERT_EQ(data.get({}, 1).cast<int>(), 5);
+    ASSERT_EQ(data.get({}, 2).cast<int>(), 7);
+    ASSERT_FALSE(data.set({}, 0, 'c'));
+    ASSERT_EQ(data.get({}, 0).cast<int>(), 3);
+    ASSERT_TRUE(data.set({}, 0, data.get({}, 0).cast<int>()+2));
+    ASSERT_TRUE(data.set({}, 1, data.get({}, 1).cast<int>()+2));
+    ASSERT_TRUE(data.set({}, 2, data.get({}, 2).cast<int>()+2));
+    ASSERT_EQ(data.get({}, 0).cast<int>(), 5);
+    ASSERT_EQ(data.get({}, 1).cast<int>(), 7);
+    ASSERT_EQ(data.get({}, 2).cast<int>(), 9);
 }
 
 TEST_F(Meta, MetaDataArray) {
@@ -1411,7 +1471,7 @@ TEST_F(Meta, MetaFuncRetVoid) {
     auto any = func.invoke(instance, 5);
 
     ASSERT_TRUE(any);
-    ASSERT_TRUE(any.can_cast<void>());
+    ASSERT_EQ(any.type(), entt::resolve<void>());
     ASSERT_EQ(func_type::value, 25);
 
     func.prop([](auto prop) {
@@ -1481,7 +1541,7 @@ TEST_F(Meta, MetaFuncStaticRetVoid) {
     auto any = func.invoke({}, 42);
 
     ASSERT_TRUE(any);
-    ASSERT_TRUE(any.can_cast<void>());
+    ASSERT_EQ(any.type(), entt::resolve<void>());
     ASSERT_EQ(func_type::value, 42);
 
     func.prop([](auto *prop) {
@@ -1501,7 +1561,9 @@ TEST_F(Meta, MetaFuncStaticRetVoid) {
 
 TEST_F(Meta, MetaFuncMetaAnyArgs) {
     auto func = entt::resolve<func_type>().func("f1"_hs);
-    auto any = func.invoke(func_type{}, entt::meta_any{3});
+    func_type instance;
+
+    auto any = func.invoke(instance, entt::meta_any{3});
 
     ASSERT_TRUE(any);
     ASSERT_EQ(any.type(), entt::resolve<int>());
@@ -1510,12 +1572,16 @@ TEST_F(Meta, MetaFuncMetaAnyArgs) {
 
 TEST_F(Meta, MetaFuncInvalidArgs) {
     auto func = entt::resolve<func_type>().func("f1"_hs);
-    ASSERT_FALSE(func.invoke(empty_type{}, entt::meta_any{'c'}));
+    empty_type instance;
+
+    ASSERT_FALSE(func.invoke(instance, entt::meta_any{'c'}));
 }
 
 TEST_F(Meta, MetaFuncCastAndConvert) {
     auto func = entt::resolve<func_type>().func("f3"_hs);
-    auto any = func.invoke(func_type{}, derived_type{}, 0, 3.);
+    func_type instance;
+
+    auto any = func.invoke(instance, derived_type{}, 0, 3.);
 
     ASSERT_TRUE(any);
     ASSERT_EQ(any.type(), entt::resolve<int>());
@@ -1640,7 +1706,7 @@ TEST_F(Meta, MetaTypeConstruct) {
     auto any = type.construct(base_type{}, 42, 'c');
 
     ASSERT_TRUE(any);
-    ASSERT_TRUE(any.can_cast<derived_type>());
+    ASSERT_TRUE(any.try_cast<derived_type>());
     ASSERT_EQ(any.cast<derived_type>().i, 42);
     ASSERT_EQ(any.cast<derived_type>().c, 'c');
 }
@@ -1650,7 +1716,7 @@ TEST_F(Meta, MetaTypeConstructMetaAnyArgs) {
     auto any = type.construct(entt::meta_any{base_type{}}, entt::meta_any{42}, entt::meta_any{'c'});
 
     ASSERT_TRUE(any);
-    ASSERT_TRUE(any.can_cast<derived_type>());
+    ASSERT_TRUE(any.try_cast<derived_type>());
     ASSERT_EQ(any.cast<derived_type>().i, 42);
     ASSERT_EQ(any.cast<derived_type>().c, 'c');
 }
@@ -1672,42 +1738,46 @@ TEST_F(Meta, MetaTypeConstructCastAndConvert) {
     auto any = type.construct(entt::meta_any{derived_type{}}, entt::meta_any{42.}, entt::meta_any{'c'});
 
     ASSERT_TRUE(any);
-    ASSERT_TRUE(any.can_cast<derived_type>());
+    ASSERT_TRUE(any.try_cast<derived_type>());
     ASSERT_EQ(any.cast<derived_type>().i, 42);
     ASSERT_EQ(any.cast<derived_type>().c, 'c');
 }
 
 TEST_F(Meta, MetaTypeDestroyDtor) {
     auto type = entt::resolve<empty_type>();
+    empty_type instance;
 
     ASSERT_EQ(empty_type::counter, 0);
-    ASSERT_TRUE(type.destroy(empty_type{}));
+    ASSERT_TRUE(type.destroy(instance));
     ASSERT_EQ(empty_type::counter, 1);
 }
 
 TEST_F(Meta, MetaTypeDestroyDtorInvalidArg) {
     auto type = entt::resolve<empty_type>();
+    auto instance = 'c';
 
     ASSERT_EQ(empty_type::counter, 0);
-    ASSERT_FALSE(type.destroy('c'));
+    ASSERT_FALSE(type.destroy(instance));
     ASSERT_EQ(empty_type::counter, 0);
 }
 
 TEST_F(Meta, MetaTypeDestroyDtorCastAndConvert) {
     auto type = entt::resolve<empty_type>();
+    fat_type instance{};
 
     ASSERT_EQ(empty_type::counter, 0);
-    ASSERT_FALSE(type.destroy(fat_type{}));
+    ASSERT_FALSE(type.destroy(instance));
     ASSERT_EQ(empty_type::counter, 0);
-    ASSERT_FALSE(entt::resolve<int>().destroy(42.));
 }
 
 TEST_F(Meta, MetaTypeDestroyNoDtor) {
-    ASSERT_TRUE(entt::resolve<char>().destroy('c'));
+    auto instance = 'c';
+    ASSERT_TRUE(entt::resolve<char>().destroy(instance));
 }
 
 TEST_F(Meta, MetaTypeDestroyNoDtorInvalidArg) {
-    ASSERT_FALSE(entt::resolve<char>().destroy(42));
+    auto instance = 42;
+    ASSERT_FALSE(entt::resolve<char>().destroy(instance));
 }
 
 TEST_F(Meta, MetaTypeDestroyNoDtorVoid) {
@@ -1715,7 +1785,8 @@ TEST_F(Meta, MetaTypeDestroyNoDtorVoid) {
 }
 
 TEST_F(Meta, MetaTypeDestroyNoDtorCastAndConvert) {
-    ASSERT_FALSE(entt::resolve<int>().destroy(42.));
+    auto instance = 42.;
+    ASSERT_FALSE(entt::resolve<int>().destroy(instance));
 }
 
 TEST_F(Meta, MetaDataFromBase) {
@@ -1853,8 +1924,8 @@ TEST_F(Meta, Unregister) {
     entt::meta_any any{42.};
 
     ASSERT_TRUE(any);
-    ASSERT_FALSE(any.can_convert<int>());
-    ASSERT_TRUE(any.can_convert<float>());
+    ASSERT_FALSE(any.convert<int>());
+    ASSERT_TRUE(any.convert<float>());
 
     ASSERT_FALSE(entt::resolve("derived"_hs));
     ASSERT_TRUE(entt::resolve("my_type"_hs));