meta: automatic conversion function to underlying type for enums (see #735)
This commit is contained in:
@@ -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.<br/>
|
||||
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.<br/>
|
||||
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<int>()
|
||||
.conv<bool>()
|
||||
.conv<char>()
|
||||
// ...
|
||||
.conv<double>();
|
||||
```
|
||||
|
||||
Repeated for each type eligible to undergo this type of conversions. This is
|
||||
both error prone and repetitive.<br/>
|
||||
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<my_enum>()
|
||||
.conv<std::underlying_type_t<my_enum>>();
|
||||
```
|
||||
|
||||
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>();
|
||||
double value = any.cast<double>();
|
||||
```
|
||||
|
||||
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<int>();
|
||||
entt::meta_any any{my_enum::a_value};
|
||||
any.allow_cast(type);
|
||||
int value = any.cast<int>();
|
||||
```
|
||||
|
||||
This should make working with arithmetic types and scoped or unscoped enums as
|
||||
easy as it is in C++.<br/>
|
||||
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<int>().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.<br/>
|
||||
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<int>()
|
||||
.conv<bool>()
|
||||
.conv<char>()
|
||||
// ...
|
||||
.conv<double>();
|
||||
```
|
||||
|
||||
Repeated for each type eligible to undergo this type of conversions. This is
|
||||
both error prone and repetitive.<br/>
|
||||
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>();
|
||||
double value = any.cast<double>();
|
||||
```
|
||||
|
||||
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<double>();
|
||||
|
||||
entt::meta_any any{42};
|
||||
any.allow_cast(type);
|
||||
double value = any.cast<double>();
|
||||
```
|
||||
|
||||
This should make working with arithmetic types as easy as it is in C++.<br/>
|
||||
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
|
||||
|
||||
@@ -1039,7 +1039,7 @@ class meta_type {
|
||||
}
|
||||
}
|
||||
|
||||
return (type->conversion_helper && other.is_arithmetic());
|
||||
return (type->conversion_helper && other.node->conversion_helper);
|
||||
}
|
||||
|
||||
template<typename... Args, auto... Index>
|
||||
@@ -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);
|
||||
|
||||
@@ -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<Type>) {
|
||||
if constexpr(std::is_arithmetic_v<Type> || std::is_enum_v<Type>) {
|
||||
return +[](const any &storage, const double *value) {
|
||||
return value ? static_cast<double>(any_cast<Type &>(const_cast<any &>(storage)) = static_cast<Type>(*value)) : static_cast<double>(any_cast<const Type &>(storage));
|
||||
};
|
||||
|
||||
@@ -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<int>());
|
||||
ASSERT_TRUE(any.allow_cast(entt::resolve<int>()));
|
||||
ASSERT_EQ(any.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(any.cast<int>(), 3);
|
||||
|
||||
@@ -867,6 +872,33 @@ TEST_F(MetaAny, ArithmeticConversion) {
|
||||
ASSERT_EQ(any.cast<char>(), 'c');
|
||||
}
|
||||
|
||||
TEST_F(MetaAny, EnumConversion) {
|
||||
entt::meta_any any{enum_class::foo};
|
||||
|
||||
ASSERT_EQ(any.type(), entt::resolve<enum_class>());
|
||||
ASSERT_EQ(any.cast<enum_class>(), enum_class::foo);
|
||||
|
||||
ASSERT_TRUE(any.allow_cast<double>());
|
||||
ASSERT_EQ(any.type(), entt::resolve<double>());
|
||||
ASSERT_EQ(any.cast<double>(), 0.);
|
||||
|
||||
any = enum_class::bar;
|
||||
|
||||
ASSERT_TRUE(any.allow_cast(entt::resolve<int>()));
|
||||
ASSERT_EQ(any.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(any.cast<int>(), 42);
|
||||
|
||||
ASSERT_TRUE(any.allow_cast<enum_class>());
|
||||
ASSERT_EQ(any.type(), entt::resolve<enum_class>());
|
||||
ASSERT_EQ(any.cast<enum_class>(), enum_class::bar);
|
||||
|
||||
any = 0;
|
||||
|
||||
ASSERT_TRUE(any.allow_cast(entt::resolve<enum_class>()));
|
||||
ASSERT_EQ(any.type(), entt::resolve<enum_class>());
|
||||
ASSERT_EQ(any.cast<enum_class>(), enum_class::foo);
|
||||
}
|
||||
|
||||
TEST_F(MetaAny, UnmanageableType) {
|
||||
unmanageable_t instance;
|
||||
auto any = entt::forward_as_meta(instance);
|
||||
|
||||
Reference in New Issue
Block a user