diff --git a/docs/md/meta.md b/docs/md/meta.md index 5a744b9cd..8c8a2e842 100644 --- a/docs/md/meta.md +++ b/docs/md/meta.md @@ -15,6 +15,7 @@ * [Template information](#template-information) * [Automatic conversions](#automatic-conversions) * [Implicitly generated default constructor](#implicitly-generated-default-constructor) + * [From void to any](#from-void-to-any) * [Policies: the more, the less](#policies-the-more-the-less) * [Named constants and enums](#named-constants-and-enums) * [Properties and meta objects](#properties-and-meta-objects) @@ -783,6 +784,24 @@ useful for building keys without knowing or having to register the actual types. In all cases, when users register default constructors, they are preferred both during searches and when the `construct` member function is invoked. +## From void to any + +Sometimes all a user has is an opaque pointer to an object of a known meta type. +It would be handy in this case to be able to construct a `meta_any` object from +them.
+For this purpose, the `meta_type` class offers a `from_void` member function +designed to convert an opaque pointer into a `meta_any`: + +```cpp +entt::meta_any any = entt::resolve(id).from_void(element); +``` + +It goes without saying that it's not possible to do a check on the actual type. +Therefore, this call can be considered as a _static cast_ with all the problems +and undefined behaviors of the case following errors.
+On the other hand, the ability to construct a `meta_any` from an opaque pointer +opens the door to some pretty interesting uses that are worth exploring. + ## Policies: the more, the less Policies are a kind of compile-time directives that can be used when registering diff --git a/src/entt/meta/meta.hpp b/src/entt/meta/meta.hpp index ba2d5d2d9..881124c53 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -1231,6 +1231,20 @@ public: return construct(arguments, sizeof...(Args)); } + /** + * @brief Wraps an opaque element of the underlying type. + * @param element A valid pointer to an element of the underlying type. + * @return A wrapper that references the given instance. + */ + meta_any from_void(void *element) { + return node->from_void ? node->from_void(element, nullptr) : meta_any{}; + } + + /*! @copydoc from_void */ + meta_any from_void(const void *element) { + return node->from_void ? node->from_void(nullptr, element) : meta_any{}; + } + /** * @brief Invokes a function given an identifier, if possible. * diff --git a/src/entt/meta/node.hpp b/src/entt/meta/node.hpp index 8ab87034f..c29856178 100644 --- a/src/entt/meta/node.hpp +++ b/src/entt/meta/node.hpp @@ -111,6 +111,7 @@ struct meta_type_node { meta_type_node *(*const remove_pointer)() noexcept; meta_any (*const default_constructor)(); double (*const conversion_helper)(void *, const void *); + meta_any (*const from_void)(void *, const void *); const meta_template_node *const templ; meta_ctor_node *ctor{nullptr}; meta_base_node *base{nullptr}; @@ -149,6 +150,16 @@ class ENTT_API meta_node { } } + [[nodiscard]] static auto *meta_from_void() noexcept { + if constexpr(std::is_same_v || std::is_array_v || std::is_function_v) { + return static_cast>(nullptr); + } else { + return +[](void *element, const void *as_const) { + return element ? meta_any{std::in_place_type, *static_cast(element)} : meta_any{std::in_place_type, *static_cast(as_const)}; + }; + } + } + [[nodiscard]] static meta_template_node *meta_template_info() noexcept { if constexpr(is_complete_v>) { static meta_template_node node{ @@ -184,6 +195,7 @@ public: &meta_node>>::resolve, meta_default_constructor(), meta_conversion_helper(), + meta_from_void(), meta_template_info() // tricks clang-format }; diff --git a/test/entt/meta/meta_type.cpp b/test/entt/meta/meta_type.cpp index 6b77b7f1e..5cc29fc46 100644 --- a/test/entt/meta/meta_type.cpp +++ b/test/entt/meta/meta_type.cpp @@ -465,6 +465,34 @@ TEST_F(MetaType, ConstructArithmeticConversion) { ASSERT_EQ(any.cast().value, 1); } +TEST_F(MetaType, FromVoid) { + using namespace entt::literals; + + ASSERT_FALSE(entt::resolve().from_void(static_cast(nullptr))); + ASSERT_FALSE(entt::resolve().from_void(static_cast(nullptr))); + + auto type = entt::resolve(); + double value = 4.2; + + auto as_void = type.from_void(static_cast(&value)); + auto as_const_void = type.from_void(static_cast(&value)); + + ASSERT_TRUE(as_void); + ASSERT_TRUE(as_const_void); + + ASSERT_EQ(as_void.type(), entt::resolve()); + ASSERT_NE(as_void.try_cast(), nullptr); + + ASSERT_EQ(as_const_void.type(), entt::resolve()); + ASSERT_EQ(as_const_void.try_cast(), nullptr); + ASSERT_NE(as_const_void.try_cast(), nullptr); + + value = 1.2; + + ASSERT_EQ(as_void.cast(), as_const_void.cast()); + ASSERT_EQ(as_void.cast(), 1.2); +} + TEST_F(MetaType, Reset) { using namespace entt::literals;