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);