diff --git a/docs/md/meta.md b/docs/md/meta.md index 9775c6b4a..bc28d24c6 100644 --- a/docs/md/meta.md +++ b/docs/md/meta.md @@ -13,8 +13,8 @@ * [Container support](#container-support) * [Pointer-like types](#pointer-like-types) * [Template information](#template-information) + * [Automatic conversions](#automatic-conversions) * [Implicitly generated default constructor](#implicitly-generated-default-constructor) - * [Automatic arithmetic conversions](#automatic-arithmetic-conversions) * [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) @@ -716,6 +716,56 @@ correspondence between real types and meta types.
Therefore, the specialization will be used as is and the information it contains will be associated with the appropriate type when required. +## Automatic conversions + +In C++, there are a number of conversions allowed between arithmetic types that +make it convenient to work with this kind of data.
+If this were to be translated into explicit registrations with the reflection +system, it would result in a long series of instructions such as the following: + +```cpp +entt::meta() + .conv() + .conv() + // ... + .conv(); +``` + +Repeated for each type eligible to undergo this type of conversions. This is +both error prone and repetitive.
+Similarly, the language allows users to silently convert unscoped enums to their +underlying types and offers what it takes to do the same for scoped enums. It +would result in the following if it were to be done explicitly: + +```cpp +entt::meta() + .conv>(); +``` + +Fortunately, all of this can also be avoided. `EnTT` offers implicit support for +these types of conversions: + +```cpp +entt::meta_any any{42}; +any.allow_cast(); +double value = any.cast(); +``` + +With no need for registration, the conversion takes place automatically under +the hood. The same goes for a call to `allow_cast` involving a meta type: + +```cpp +entt::meta_type type = entt::resolve(); +entt::meta_any any{my_enum::a_value}; +any.allow_cast(type); +int value = any.cast(); +``` + +This should make working with arithmetic types and scoped or unscoped enums as +easy as it is in C++.
+It's also worth noting that it's still possible to set up conversion functions +manually and these will always be preferred over the automatic ones. + ## Implicitly generated default constructor In many cases, it's useful to be able to create objects of default constructible @@ -756,47 +806,6 @@ entt::meta_ctor ctor = entt::resolve().ctor<>(); In other terms, `ctor` is an invalid meta object unless users explicitly registered a meta constructor that takes no arguments for the `int` type. -## Automatic arithmetic conversions - -In C++, there are a number of conversions allowed between arithmetic types that -make it convenient to work with this kind of data.
-If this were to be translated into explicit registrations with the reflection -system, it would result in a long series of instructions such as the following: - -```cpp -entt::meta() - .conv() - .conv() - // ... - .conv(); -``` - -Repeated for each type eligible to undergo this type of conversions. This is -both error prone and repetitive.
-Fortunately, all of this can also be avoided. `EnTT` offers implicit support for -these types of conversions: - -```cpp -entt::meta_any any{42}; -any.allow_cast(); -double value = any.cast(); -``` - -With no need for registration, the conversion takes place automatically under -the hood. The same goes for a call to `allow_cast` involving a meta type: - -```cpp -entt::meta_type type = entt::resolve(); - -entt::meta_any any{42}; -any.allow_cast(type); -double value = any.cast(); -``` - -This should make working with arithmetic types as easy as it is in C++.
-It's also worth noting that it's still possible to set up conversion functions -manually and these will always be preferred over the automatic ones. - ## 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 075761ad1..37024ef7f 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -1039,7 +1039,7 @@ class meta_type { } } - return (type->conversion_helper && other.is_arithmetic()); + return (type->conversion_helper && other.node->conversion_helper); } template @@ -1507,8 +1507,8 @@ bool meta_any::set(const id_type id, Type &&value) { return as_ref(); } else if(const auto * const conv = internal::visit<&internal::meta_type_node::conv>([info](const auto *curr) { return curr->type->info == info; }, node); conv) { return conv->conv(storage.data()); - } else if(type.is_arithmetic() && node->conversion_helper) { - // exploits the fact that arithmetic types are also default constructibles in all cases + } else if((type.is_arithmetic() || type.is_enum()) && node->conversion_helper) { + // exploits the fact that arithmetic types and enums are also default constructible auto other = type.construct(); const double value = node->conversion_helper(storage, nullptr); other.node->conversion_helper(other.storage, &value); diff --git a/src/entt/meta/node.hpp b/src/entt/meta/node.hpp index b3bc09325..64d702e1e 100644 --- a/src/entt/meta/node.hpp +++ b/src/entt/meta/node.hpp @@ -150,7 +150,7 @@ class ENTT_API meta_node { } [[nodiscard]] static decltype(meta_type_node::conversion_helper) meta_conversion_helper() ENTT_NOEXCEPT { - if constexpr(std::is_arithmetic_v) { + if constexpr(std::is_arithmetic_v || std::is_enum_v) { return +[](const any &storage, const double *value) { return value ? static_cast(any_cast(const_cast(storage)) = static_cast(*value)) : static_cast(any_cast(storage)); }; diff --git a/test/entt/meta/meta_any.cpp b/test/entt/meta/meta_any.cpp index 1c3071d50..473ba0c75 100644 --- a/test/entt/meta/meta_any.cpp +++ b/test/entt/meta/meta_any.cpp @@ -46,6 +46,11 @@ struct fat_t: empty_t { double value[4]; }; +enum class enum_class: unsigned short int { + foo = 0u, + bar = 42u +}; + struct not_comparable_t { bool operator==(const not_comparable_t &) const = delete; }; @@ -852,7 +857,7 @@ TEST_F(MetaAny, ArithmeticConversion) { any = 3.1; - ASSERT_TRUE(any.allow_cast()); + ASSERT_TRUE(any.allow_cast(entt::resolve())); ASSERT_EQ(any.type(), entt::resolve()); ASSERT_EQ(any.cast(), 3); @@ -867,6 +872,33 @@ TEST_F(MetaAny, ArithmeticConversion) { ASSERT_EQ(any.cast(), 'c'); } +TEST_F(MetaAny, EnumConversion) { + entt::meta_any any{enum_class::foo}; + + ASSERT_EQ(any.type(), entt::resolve()); + ASSERT_EQ(any.cast(), enum_class::foo); + + ASSERT_TRUE(any.allow_cast()); + ASSERT_EQ(any.type(), entt::resolve()); + ASSERT_EQ(any.cast(), 0.); + + any = enum_class::bar; + + ASSERT_TRUE(any.allow_cast(entt::resolve())); + ASSERT_EQ(any.type(), entt::resolve()); + ASSERT_EQ(any.cast(), 42); + + ASSERT_TRUE(any.allow_cast()); + ASSERT_EQ(any.type(), entt::resolve()); + ASSERT_EQ(any.cast(), enum_class::bar); + + any = 0; + + ASSERT_TRUE(any.allow_cast(entt::resolve())); + ASSERT_EQ(any.type(), entt::resolve()); + ASSERT_EQ(any.cast(), enum_class::foo); +} + TEST_F(MetaAny, UnmanageableType) { unmanageable_t instance; auto any = entt::forward_as_meta(instance);