2021-12-18 22:59:55 +00:00
|
|
|
#![doc = include_str!("../README.md")]
|
|
|
|
|
2022-05-13 01:13:30 +00:00
|
|
|
mod array;
|
bevy_reflect: Add statically available type info for reflected types (#4042)
# Objective
> Resolves #4504
It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance.
## Solution
Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait:
```rust
pub trait Typed: Reflect {
fn type_info() -> &'static TypeInfo;
}
```
> Note: This trait was made separate from `Reflect` due to `Sized` restrictions.
If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically.
If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered).
### Usage
Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information.
```rust
#[derive(Reflect)]
struct MyTupleStruct(usize, i32, MyStruct);
let info = MyTupleStruct::type_info();
if let TypeInfo::TupleStruct(info) = info {
assert!(info.is::<MyTupleStruct>());
assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name());
assert!(info.field_at(1).unwrap().is::<i32>());
} else {
panic!("Expected `TypeInfo::TupleStruct`");
}
```
### Manual Implementations
It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls):
```rust
use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField};
use bevy_reflect::utility::TypeInfoCell;
struct Foo<T: Reflect>(T);
impl<T: Reflect> Typed for Foo<T> {
fn type_info() -> &'static TypeInfo {
static CELL: TypeInfoCell = TypeInfoCell::generic();
CELL.get_or_insert::<Self, _>(|| {
let fields = [UnnamedField::new::<T>()];
let info = TupleStructInfo::new::<Self>(&fields);
TypeInfo::TupleStruct(info)
})
}
}
```
## Benefits
One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like:
```rust
#[derive(Reflect)]
struct MyType {
foo: usize,
bar: Vec<String>
}
// RON to be deserialized:
(
type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object
value: {
// "foo" is a value type matching "usize"
"foo": 123,
// "bar" is a list type matching "Vec<String>" with item type "String"
"bar": ["a", "b", "c"]
}
)
```
Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data).
Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry.
## Discussion
Some items to discuss:
1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way?
2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically).
4. General usefulness of this change, including missing/unnecessary parts.
5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.)
## Compile Tests
I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR (aa5178e7736a6f8252e10e543e52722107649d3f) and main (c309acd4322b1c3b2089e247a2d28b938eb7b56d).
<details>
<summary>See More</summary>
The test project included 250 of the following structs (as well as a few other structs):
```rust
#[derive(Default)]
#[cfg_attr(feature = "reflect", derive(Reflect))]
#[cfg_attr(feature = "from_reflect", derive(FromReflect))]
pub struct Big001 {
inventory: Inventory,
foo: usize,
bar: String,
baz: ItemDescriptor,
items: [Item; 20],
hello: Option<String>,
world: HashMap<i32, String>,
okay: (isize, usize, /* wesize */),
nope: ((String, String), (f32, f32)),
blah: Cow<'static, str>,
}
```
> I don't know if the compiler can optimize all these duplicate structs away, but I think it's fine either way. We're comparing times, not finding the absolute worst-case time.
I only ran each build 3 times using `cargo build --timings` (thank you @devil-ira), each of which were preceeded by a `cargo clean --package bevy_reflect_compile_test`.
Here are the times I got:
| Test | Test 1 | Test 2 | Test 3 | Average |
| -------------------------------- | ------ | ------ | ------ | ------- |
| Main | 1.7s | 3.1s | 1.9s | 2.33s |
| Main + `Reflect` | 8.3s | 8.6s | 8.1s | 8.33s |
| Main + `Reflect` + `FromReflect` | 11.6s | 11.8s | 13.8s | 12.4s |
| PR | 3.5s | 1.8s | 1.9s | 2.4s |
| PR + `Reflect` | 9.2s | 8.8s | 9.3s | 9.1s |
| PR + `Reflect` + `FromReflect` | 12.9s | 12.3s | 12.5s | 12.56s |
</details>
---
## Future Work
Even though everything could probably be made `const`, we unfortunately can't. This is because `TypeId::of::<T>()` is not yet `const` (see https://github.com/rust-lang/rust/issues/77125). When it does get stabilized, it would probably be worth coming back and making things `const`.
Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-06-09 21:18:15 +00:00
|
|
|
mod fields;
|
2020-11-28 00:39:59 +00:00
|
|
|
mod list;
|
|
|
|
mod map;
|
|
|
|
mod path;
|
|
|
|
mod reflect;
|
|
|
|
mod struct_trait;
|
2021-01-08 03:50:09 +00:00
|
|
|
mod tuple;
|
2020-11-28 00:39:59 +00:00
|
|
|
mod tuple_struct;
|
bevy_reflect: Add statically available type info for reflected types (#4042)
# Objective
> Resolves #4504
It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance.
## Solution
Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait:
```rust
pub trait Typed: Reflect {
fn type_info() -> &'static TypeInfo;
}
```
> Note: This trait was made separate from `Reflect` due to `Sized` restrictions.
If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically.
If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered).
### Usage
Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information.
```rust
#[derive(Reflect)]
struct MyTupleStruct(usize, i32, MyStruct);
let info = MyTupleStruct::type_info();
if let TypeInfo::TupleStruct(info) = info {
assert!(info.is::<MyTupleStruct>());
assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name());
assert!(info.field_at(1).unwrap().is::<i32>());
} else {
panic!("Expected `TypeInfo::TupleStruct`");
}
```
### Manual Implementations
It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls):
```rust
use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField};
use bevy_reflect::utility::TypeInfoCell;
struct Foo<T: Reflect>(T);
impl<T: Reflect> Typed for Foo<T> {
fn type_info() -> &'static TypeInfo {
static CELL: TypeInfoCell = TypeInfoCell::generic();
CELL.get_or_insert::<Self, _>(|| {
let fields = [UnnamedField::new::<T>()];
let info = TupleStructInfo::new::<Self>(&fields);
TypeInfo::TupleStruct(info)
})
}
}
```
## Benefits
One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like:
```rust
#[derive(Reflect)]
struct MyType {
foo: usize,
bar: Vec<String>
}
// RON to be deserialized:
(
type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object
value: {
// "foo" is a value type matching "usize"
"foo": 123,
// "bar" is a list type matching "Vec<String>" with item type "String"
"bar": ["a", "b", "c"]
}
)
```
Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data).
Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry.
## Discussion
Some items to discuss:
1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way?
2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically).
4. General usefulness of this change, including missing/unnecessary parts.
5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.)
## Compile Tests
I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR (aa5178e7736a6f8252e10e543e52722107649d3f) and main (c309acd4322b1c3b2089e247a2d28b938eb7b56d).
<details>
<summary>See More</summary>
The test project included 250 of the following structs (as well as a few other structs):
```rust
#[derive(Default)]
#[cfg_attr(feature = "reflect", derive(Reflect))]
#[cfg_attr(feature = "from_reflect", derive(FromReflect))]
pub struct Big001 {
inventory: Inventory,
foo: usize,
bar: String,
baz: ItemDescriptor,
items: [Item; 20],
hello: Option<String>,
world: HashMap<i32, String>,
okay: (isize, usize, /* wesize */),
nope: ((String, String), (f32, f32)),
blah: Cow<'static, str>,
}
```
> I don't know if the compiler can optimize all these duplicate structs away, but I think it's fine either way. We're comparing times, not finding the absolute worst-case time.
I only ran each build 3 times using `cargo build --timings` (thank you @devil-ira), each of which were preceeded by a `cargo clean --package bevy_reflect_compile_test`.
Here are the times I got:
| Test | Test 1 | Test 2 | Test 3 | Average |
| -------------------------------- | ------ | ------ | ------ | ------- |
| Main | 1.7s | 3.1s | 1.9s | 2.33s |
| Main + `Reflect` | 8.3s | 8.6s | 8.1s | 8.33s |
| Main + `Reflect` + `FromReflect` | 11.6s | 11.8s | 13.8s | 12.4s |
| PR | 3.5s | 1.8s | 1.9s | 2.4s |
| PR + `Reflect` | 9.2s | 8.8s | 9.3s | 9.1s |
| PR + `Reflect` + `FromReflect` | 12.9s | 12.3s | 12.5s | 12.56s |
</details>
---
## Future Work
Even though everything could probably be made `const`, we unfortunately can't. This is because `TypeId::of::<T>()` is not yet `const` (see https://github.com/rust-lang/rust/issues/77125). When it does get stabilized, it would probably be worth coming back and making things `const`.
Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-06-09 21:18:15 +00:00
|
|
|
mod type_info;
|
2020-11-28 00:39:59 +00:00
|
|
|
mod type_registry;
|
|
|
|
mod type_uuid;
|
|
|
|
mod impls {
|
|
|
|
#[cfg(feature = "glam")]
|
|
|
|
mod glam;
|
|
|
|
#[cfg(feature = "smallvec")]
|
|
|
|
mod smallvec;
|
|
|
|
mod std;
|
|
|
|
|
|
|
|
#[cfg(feature = "glam")]
|
|
|
|
pub use self::glam::*;
|
|
|
|
#[cfg(feature = "smallvec")]
|
|
|
|
pub use self::smallvec::*;
|
|
|
|
pub use self::std::*;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub mod serde;
|
2022-05-03 19:20:13 +00:00
|
|
|
pub mod std_traits;
|
bevy_reflect: Add statically available type info for reflected types (#4042)
# Objective
> Resolves #4504
It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance.
## Solution
Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait:
```rust
pub trait Typed: Reflect {
fn type_info() -> &'static TypeInfo;
}
```
> Note: This trait was made separate from `Reflect` due to `Sized` restrictions.
If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically.
If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered).
### Usage
Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information.
```rust
#[derive(Reflect)]
struct MyTupleStruct(usize, i32, MyStruct);
let info = MyTupleStruct::type_info();
if let TypeInfo::TupleStruct(info) = info {
assert!(info.is::<MyTupleStruct>());
assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name());
assert!(info.field_at(1).unwrap().is::<i32>());
} else {
panic!("Expected `TypeInfo::TupleStruct`");
}
```
### Manual Implementations
It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls):
```rust
use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField};
use bevy_reflect::utility::TypeInfoCell;
struct Foo<T: Reflect>(T);
impl<T: Reflect> Typed for Foo<T> {
fn type_info() -> &'static TypeInfo {
static CELL: TypeInfoCell = TypeInfoCell::generic();
CELL.get_or_insert::<Self, _>(|| {
let fields = [UnnamedField::new::<T>()];
let info = TupleStructInfo::new::<Self>(&fields);
TypeInfo::TupleStruct(info)
})
}
}
```
## Benefits
One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like:
```rust
#[derive(Reflect)]
struct MyType {
foo: usize,
bar: Vec<String>
}
// RON to be deserialized:
(
type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object
value: {
// "foo" is a value type matching "usize"
"foo": 123,
// "bar" is a list type matching "Vec<String>" with item type "String"
"bar": ["a", "b", "c"]
}
)
```
Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data).
Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry.
## Discussion
Some items to discuss:
1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way?
2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically).
4. General usefulness of this change, including missing/unnecessary parts.
5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.)
## Compile Tests
I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR (aa5178e7736a6f8252e10e543e52722107649d3f) and main (c309acd4322b1c3b2089e247a2d28b938eb7b56d).
<details>
<summary>See More</summary>
The test project included 250 of the following structs (as well as a few other structs):
```rust
#[derive(Default)]
#[cfg_attr(feature = "reflect", derive(Reflect))]
#[cfg_attr(feature = "from_reflect", derive(FromReflect))]
pub struct Big001 {
inventory: Inventory,
foo: usize,
bar: String,
baz: ItemDescriptor,
items: [Item; 20],
hello: Option<String>,
world: HashMap<i32, String>,
okay: (isize, usize, /* wesize */),
nope: ((String, String), (f32, f32)),
blah: Cow<'static, str>,
}
```
> I don't know if the compiler can optimize all these duplicate structs away, but I think it's fine either way. We're comparing times, not finding the absolute worst-case time.
I only ran each build 3 times using `cargo build --timings` (thank you @devil-ira), each of which were preceeded by a `cargo clean --package bevy_reflect_compile_test`.
Here are the times I got:
| Test | Test 1 | Test 2 | Test 3 | Average |
| -------------------------------- | ------ | ------ | ------ | ------- |
| Main | 1.7s | 3.1s | 1.9s | 2.33s |
| Main + `Reflect` | 8.3s | 8.6s | 8.1s | 8.33s |
| Main + `Reflect` + `FromReflect` | 11.6s | 11.8s | 13.8s | 12.4s |
| PR | 3.5s | 1.8s | 1.9s | 2.4s |
| PR + `Reflect` | 9.2s | 8.8s | 9.3s | 9.1s |
| PR + `Reflect` + `FromReflect` | 12.9s | 12.3s | 12.5s | 12.56s |
</details>
---
## Future Work
Even though everything could probably be made `const`, we unfortunately can't. This is because `TypeId::of::<T>()` is not yet `const` (see https://github.com/rust-lang/rust/issues/77125). When it does get stabilized, it would probably be worth coming back and making things `const`.
Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-06-09 21:18:15 +00:00
|
|
|
pub mod utility;
|
2022-05-03 19:20:13 +00:00
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
pub mod prelude {
|
2022-05-03 19:20:13 +00:00
|
|
|
pub use crate::std_traits::*;
|
2021-04-27 18:29:33 +00:00
|
|
|
#[doc(hidden)]
|
2020-11-28 00:39:59 +00:00
|
|
|
pub use crate::{
|
2022-06-20 17:18:58 +00:00
|
|
|
reflect_trait, GetField, GetTupleStructField, Reflect, ReflectDeserialize,
|
|
|
|
ReflectSerialize, Struct, TupleStruct,
|
2020-11-28 00:39:59 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-05-13 01:13:30 +00:00
|
|
|
pub use array::*;
|
bevy_reflect: Add statically available type info for reflected types (#4042)
# Objective
> Resolves #4504
It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance.
## Solution
Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait:
```rust
pub trait Typed: Reflect {
fn type_info() -> &'static TypeInfo;
}
```
> Note: This trait was made separate from `Reflect` due to `Sized` restrictions.
If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically.
If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered).
### Usage
Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information.
```rust
#[derive(Reflect)]
struct MyTupleStruct(usize, i32, MyStruct);
let info = MyTupleStruct::type_info();
if let TypeInfo::TupleStruct(info) = info {
assert!(info.is::<MyTupleStruct>());
assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name());
assert!(info.field_at(1).unwrap().is::<i32>());
} else {
panic!("Expected `TypeInfo::TupleStruct`");
}
```
### Manual Implementations
It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls):
```rust
use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField};
use bevy_reflect::utility::TypeInfoCell;
struct Foo<T: Reflect>(T);
impl<T: Reflect> Typed for Foo<T> {
fn type_info() -> &'static TypeInfo {
static CELL: TypeInfoCell = TypeInfoCell::generic();
CELL.get_or_insert::<Self, _>(|| {
let fields = [UnnamedField::new::<T>()];
let info = TupleStructInfo::new::<Self>(&fields);
TypeInfo::TupleStruct(info)
})
}
}
```
## Benefits
One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like:
```rust
#[derive(Reflect)]
struct MyType {
foo: usize,
bar: Vec<String>
}
// RON to be deserialized:
(
type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object
value: {
// "foo" is a value type matching "usize"
"foo": 123,
// "bar" is a list type matching "Vec<String>" with item type "String"
"bar": ["a", "b", "c"]
}
)
```
Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data).
Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry.
## Discussion
Some items to discuss:
1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way?
2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically).
4. General usefulness of this change, including missing/unnecessary parts.
5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.)
## Compile Tests
I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR (aa5178e7736a6f8252e10e543e52722107649d3f) and main (c309acd4322b1c3b2089e247a2d28b938eb7b56d).
<details>
<summary>See More</summary>
The test project included 250 of the following structs (as well as a few other structs):
```rust
#[derive(Default)]
#[cfg_attr(feature = "reflect", derive(Reflect))]
#[cfg_attr(feature = "from_reflect", derive(FromReflect))]
pub struct Big001 {
inventory: Inventory,
foo: usize,
bar: String,
baz: ItemDescriptor,
items: [Item; 20],
hello: Option<String>,
world: HashMap<i32, String>,
okay: (isize, usize, /* wesize */),
nope: ((String, String), (f32, f32)),
blah: Cow<'static, str>,
}
```
> I don't know if the compiler can optimize all these duplicate structs away, but I think it's fine either way. We're comparing times, not finding the absolute worst-case time.
I only ran each build 3 times using `cargo build --timings` (thank you @devil-ira), each of which were preceeded by a `cargo clean --package bevy_reflect_compile_test`.
Here are the times I got:
| Test | Test 1 | Test 2 | Test 3 | Average |
| -------------------------------- | ------ | ------ | ------ | ------- |
| Main | 1.7s | 3.1s | 1.9s | 2.33s |
| Main + `Reflect` | 8.3s | 8.6s | 8.1s | 8.33s |
| Main + `Reflect` + `FromReflect` | 11.6s | 11.8s | 13.8s | 12.4s |
| PR | 3.5s | 1.8s | 1.9s | 2.4s |
| PR + `Reflect` | 9.2s | 8.8s | 9.3s | 9.1s |
| PR + `Reflect` + `FromReflect` | 12.9s | 12.3s | 12.5s | 12.56s |
</details>
---
## Future Work
Even though everything could probably be made `const`, we unfortunately can't. This is because `TypeId::of::<T>()` is not yet `const` (see https://github.com/rust-lang/rust/issues/77125). When it does get stabilized, it would probably be worth coming back and making things `const`.
Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-06-09 21:18:15 +00:00
|
|
|
pub use fields::*;
|
2020-11-28 00:39:59 +00:00
|
|
|
pub use impls::*;
|
|
|
|
pub use list::*;
|
|
|
|
pub use map::*;
|
|
|
|
pub use path::*;
|
|
|
|
pub use reflect::*;
|
|
|
|
pub use struct_trait::*;
|
2021-01-08 03:50:09 +00:00
|
|
|
pub use tuple::*;
|
2020-11-28 00:39:59 +00:00
|
|
|
pub use tuple_struct::*;
|
bevy_reflect: Add statically available type info for reflected types (#4042)
# Objective
> Resolves #4504
It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance.
## Solution
Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait:
```rust
pub trait Typed: Reflect {
fn type_info() -> &'static TypeInfo;
}
```
> Note: This trait was made separate from `Reflect` due to `Sized` restrictions.
If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically.
If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered).
### Usage
Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information.
```rust
#[derive(Reflect)]
struct MyTupleStruct(usize, i32, MyStruct);
let info = MyTupleStruct::type_info();
if let TypeInfo::TupleStruct(info) = info {
assert!(info.is::<MyTupleStruct>());
assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name());
assert!(info.field_at(1).unwrap().is::<i32>());
} else {
panic!("Expected `TypeInfo::TupleStruct`");
}
```
### Manual Implementations
It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls):
```rust
use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField};
use bevy_reflect::utility::TypeInfoCell;
struct Foo<T: Reflect>(T);
impl<T: Reflect> Typed for Foo<T> {
fn type_info() -> &'static TypeInfo {
static CELL: TypeInfoCell = TypeInfoCell::generic();
CELL.get_or_insert::<Self, _>(|| {
let fields = [UnnamedField::new::<T>()];
let info = TupleStructInfo::new::<Self>(&fields);
TypeInfo::TupleStruct(info)
})
}
}
```
## Benefits
One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like:
```rust
#[derive(Reflect)]
struct MyType {
foo: usize,
bar: Vec<String>
}
// RON to be deserialized:
(
type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object
value: {
// "foo" is a value type matching "usize"
"foo": 123,
// "bar" is a list type matching "Vec<String>" with item type "String"
"bar": ["a", "b", "c"]
}
)
```
Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data).
Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry.
## Discussion
Some items to discuss:
1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way?
2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically).
4. General usefulness of this change, including missing/unnecessary parts.
5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.)
## Compile Tests
I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR (aa5178e7736a6f8252e10e543e52722107649d3f) and main (c309acd4322b1c3b2089e247a2d28b938eb7b56d).
<details>
<summary>See More</summary>
The test project included 250 of the following structs (as well as a few other structs):
```rust
#[derive(Default)]
#[cfg_attr(feature = "reflect", derive(Reflect))]
#[cfg_attr(feature = "from_reflect", derive(FromReflect))]
pub struct Big001 {
inventory: Inventory,
foo: usize,
bar: String,
baz: ItemDescriptor,
items: [Item; 20],
hello: Option<String>,
world: HashMap<i32, String>,
okay: (isize, usize, /* wesize */),
nope: ((String, String), (f32, f32)),
blah: Cow<'static, str>,
}
```
> I don't know if the compiler can optimize all these duplicate structs away, but I think it's fine either way. We're comparing times, not finding the absolute worst-case time.
I only ran each build 3 times using `cargo build --timings` (thank you @devil-ira), each of which were preceeded by a `cargo clean --package bevy_reflect_compile_test`.
Here are the times I got:
| Test | Test 1 | Test 2 | Test 3 | Average |
| -------------------------------- | ------ | ------ | ------ | ------- |
| Main | 1.7s | 3.1s | 1.9s | 2.33s |
| Main + `Reflect` | 8.3s | 8.6s | 8.1s | 8.33s |
| Main + `Reflect` + `FromReflect` | 11.6s | 11.8s | 13.8s | 12.4s |
| PR | 3.5s | 1.8s | 1.9s | 2.4s |
| PR + `Reflect` | 9.2s | 8.8s | 9.3s | 9.1s |
| PR + `Reflect` + `FromReflect` | 12.9s | 12.3s | 12.5s | 12.56s |
</details>
---
## Future Work
Even though everything could probably be made `const`, we unfortunately can't. This is because `TypeId::of::<T>()` is not yet `const` (see https://github.com/rust-lang/rust/issues/77125). When it does get stabilized, it would probably be worth coming back and making things `const`.
Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-06-09 21:18:15 +00:00
|
|
|
pub use type_info::*;
|
2020-11-28 00:39:59 +00:00
|
|
|
pub use type_registry::*;
|
|
|
|
pub use type_uuid::*;
|
|
|
|
|
|
|
|
pub use bevy_reflect_derive::*;
|
|
|
|
pub use erased_serde;
|
|
|
|
|
2022-04-26 19:41:25 +00:00
|
|
|
#[doc(hidden)]
|
|
|
|
pub mod __macro_exports {
|
|
|
|
use crate::Uuid;
|
|
|
|
|
|
|
|
/// Generates a new UUID from the given UUIDs `a` and `b`,
|
|
|
|
/// where the bytes are generated by a bitwise `a ^ b.rotate_right(1)`.
|
|
|
|
/// The generated UUID will be a `UUIDv4` (meaning that the bytes should be random, not e.g. derived from the system time).
|
|
|
|
#[allow(clippy::unusual_byte_groupings)] // unusual byte grouping is meant to signal the relevant bits
|
|
|
|
pub const fn generate_composite_uuid(a: Uuid, b: Uuid) -> Uuid {
|
|
|
|
let mut new = [0; 16];
|
|
|
|
let mut i = 0;
|
|
|
|
while i < new.len() {
|
|
|
|
// rotating ensures different uuids for A<B<C>> and B<A<C>> because: A ^ (B ^ C) = B ^ (A ^ C)
|
|
|
|
// notice that you have to rotate the second parameter: A.rr ^ (B.rr ^ C) = B.rr ^ (A.rr ^ C)
|
|
|
|
// Solution: A ^ (B ^ C.rr).rr != B ^ (A ^ C.rr).rr
|
|
|
|
new[i] = a.as_bytes()[i] ^ b.as_bytes()[i].rotate_right(1);
|
|
|
|
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Version: the most significant 4 bits in the 6th byte: 11110000
|
|
|
|
new[6] = new[6] & 0b0000_1111 | 0b0100_0000; // set version to v4
|
|
|
|
|
|
|
|
// Variant: the most significant 3 bits in the 8th byte: 11100000
|
|
|
|
new[8] = new[8] & 0b000_11111 | 0b100_00000; // set variant to rfc4122
|
|
|
|
|
|
|
|
Uuid::from_bytes(new)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
#[cfg(test)]
|
2021-02-22 08:42:19 +00:00
|
|
|
#[allow(clippy::blacklisted_name, clippy::approx_constant)]
|
2020-11-28 00:39:59 +00:00
|
|
|
mod tests {
|
2022-05-09 16:32:15 +00:00
|
|
|
#[cfg(feature = "glam")]
|
|
|
|
use ::glam::{vec3, Vec3};
|
2020-11-28 00:39:59 +00:00
|
|
|
use ::serde::de::DeserializeSeed;
|
|
|
|
use bevy_utils::HashMap;
|
|
|
|
use ron::{
|
|
|
|
ser::{to_string_pretty, PrettyConfig},
|
|
|
|
Deserializer,
|
|
|
|
};
|
bevy_reflect: Improve debug formatting for reflected types (#4218)
# Objective
Debugging reflected types can be somewhat frustrating since all `dyn Reflect` trait objects return something like `Reflect(core::option::Option<alloc::string::String>)`.
It would be much nicer to be able to see the actual value— or even use a custom `Debug` implementation.
## Solution
Added `Reflect::debug` which allows users to customize the debug output. It sets defaults for all `ReflectRef` subtraits and falls back to `Reflect(type_name)` if no `Debug` implementation was registered.
To register a custom `Debug` impl, users can add `#[reflect(Debug)]` like they can with other traits.
### Example
Using the following structs:
```rust
#[derive(Reflect)]
pub struct Foo {
a: usize,
nested: Bar,
#[reflect(ignore)]
_ignored: NonReflectedValue,
}
#[derive(Reflect)]
pub struct Bar {
value: Vec2,
tuple_value: (i32, String),
list_value: Vec<usize>,
// We can't determine debug formatting for Option<T> yet
unknown_value: Option<String>,
custom_debug: CustomDebug
}
#[derive(Reflect)]
#[reflect(Debug)]
struct CustomDebug;
impl Debug for CustomDebug {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "This is a custom debug!")
}
}
pub struct NonReflectedValue {
_a: usize,
}
```
We can do:
```rust
let value = Foo {
a: 1,
_ignored: NonReflectedValue { _a: 10 },
nested: Bar {
value: Vec2::new(1.23, 3.21),
tuple_value: (123, String::from("Hello")),
list_value: vec![1, 2, 3],
unknown_value: Some(String::from("World")),
custom_debug: CustomDebug
},
};
let reflected_value: &dyn Reflect = &value;
println!("{:#?}", reflected_value)
```
Which results in:
```rust
Foo {
a: 2,
nested: Bar {
value: Vec2(
1.23,
3.21,
),
tuple_value: (
123,
"Hello",
),
list_value: [
1,
2,
3,
],
unknown_value: Reflect(core::option::Option<alloc::string::String>),
custom_debug: This is a custom debug!,
},
}
```
Notice that neither `Foo` nor `Bar` implement `Debug`, yet we can still deduce it. This might be a concern if we're worried about leaking internal values. If it is, we might want to consider a way to exclude fields (possibly with a `#[reflect(hide)]` macro) or make it purely opt in (as opposed to the default implementation automatically handled by ReflectRef subtraits).
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2022-05-30 16:41:31 +00:00
|
|
|
use std::fmt::{Debug, Formatter};
|
2020-11-28 00:39:59 +00:00
|
|
|
|
bevy_reflect: Add `#[reflect(default)]` attribute for `FromReflect` (#4140)
# Objective
Currently, `FromReflect` makes a couple assumptions:
* Ignored fields must implement `Default`
* Active fields must implement `FromReflect`
* The reflected must be fully populated for active fields (can't use an empty `DynamicStruct`)
However, one or both of these requirements might be unachievable, such as for external types. In these cases, it might be nice to tell `FromReflect` to use a custom default.
## Solution
Added the `#[reflect(default)]` derive helper attribute. This attribute can be applied to any field (ignored or not) and will allow a default value to be specified in place of the regular `from_reflect()` call.
It takes two forms: `#[reflect(default)]` and `#[reflect(default = "some_func")]`. The former specifies that `Default::default()` should be used while the latter specifies that `some_func()` should be used. This is pretty much [how serde does it](https://serde.rs/field-attrs.html#default).
### Example
```rust
#[derive(Reflect, FromReflect)]
struct MyStruct {
// Use `Default::default()`
#[reflect(default)]
foo: String,
// Use `get_bar_default()`
#[reflect(default = "get_bar_default")]
#[reflect(ignore)]
bar: usize,
}
fn get_bar_default() -> usize {
123
}
```
### Active Fields
As an added benefit, this also allows active fields to be completely missing from their dynamic object. This is because the attribute tells `FromReflect` how to handle missing active fields (it still tries to use `from_reflect` first so the `FromReflect` trait is still required).
```rust
let dyn_struct = DynamicStruct::default();
// We can do this without actually including the active fields since they have `#[reflect(default)]`
let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct);
```
### Container Defaults
Also, with the addition of #3733, people will likely start adding `#[reflect(Default)]` to their types now. Just like with the fields, we can use this to mark the entire container as "defaultable". This grants us the ability to completely remove the field markers altogether if our type implements `Default` (and we're okay with fields using that instead of their own `Default` impls):
```rust
#[derive(Reflect, FromReflect)]
#[reflect(Default)]
struct MyStruct {
foo: String,
#[reflect(ignore)]
bar: usize,
}
impl Default for MyStruct {
fn default() -> Self {
Self {
foo: String::from("Hello"),
bar: 123,
}
}
}
// Again, we can now construct this from nothing pretty much
let dyn_struct = DynamicStruct::default();
let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct);
```
Now if _any_ field is missing when using `FromReflect`, we simply fallback onto the container's `Default` implementation.
This behavior can be completely overridden on a per-field basis, of course, by simply defining those same field attributes like before.
### Related
* #3733
* #1395
* #2377
---
## Changelog
* Added `#[reflect(default)]` field attribute for `FromReflect`
* Allows missing fields to be given a default value when using `FromReflect`
* `#[reflect(default)]` - Use the field's `Default` implementation
* `#[reflect(default = "some_fn")]` - Use a custom function to get the default value
* Allow `#[reflect(Default)]` to have a secondary usage as a container attribute
* Allows missing fields to be given a default value based on the container's `Default` impl when using `FromReflect`
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2022-05-30 19:06:25 +00:00
|
|
|
use super::prelude::*;
|
2020-11-28 00:39:59 +00:00
|
|
|
use super::*;
|
2021-05-19 19:03:36 +00:00
|
|
|
use crate as bevy_reflect;
|
|
|
|
use crate::serde::{ReflectDeserializer, ReflectSerializer};
|
2021-02-22 08:42:19 +00:00
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
#[test]
|
|
|
|
fn reflect_struct() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
b: f32,
|
|
|
|
c: Bar,
|
|
|
|
}
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Bar {
|
|
|
|
x: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut foo = Foo {
|
|
|
|
a: 42,
|
|
|
|
b: 3.14,
|
|
|
|
c: Bar { x: 1 },
|
|
|
|
};
|
|
|
|
|
|
|
|
let a = *foo.get_field::<u32>("a").unwrap();
|
|
|
|
assert_eq!(a, 42);
|
|
|
|
|
|
|
|
*foo.get_field_mut::<u32>("a").unwrap() += 1;
|
|
|
|
assert_eq!(foo.a, 43);
|
|
|
|
|
|
|
|
let bar = foo.get_field::<Bar>("c").unwrap();
|
|
|
|
assert_eq!(bar.x, 1);
|
|
|
|
|
|
|
|
// nested retrieval
|
|
|
|
let c = foo.field("c").unwrap();
|
|
|
|
if let ReflectRef::Struct(value) = c.reflect_ref() {
|
|
|
|
assert_eq!(*value.get_field::<u32>("x").unwrap(), 1);
|
|
|
|
} else {
|
2020-12-02 19:31:16 +00:00
|
|
|
panic!("Expected a struct.");
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// patch Foo with a dynamic struct
|
|
|
|
let mut dynamic_struct = DynamicStruct::default();
|
|
|
|
dynamic_struct.insert("a", 123u32);
|
|
|
|
dynamic_struct.insert("should_be_ignored", 456);
|
|
|
|
|
|
|
|
foo.apply(&dynamic_struct);
|
|
|
|
assert_eq!(foo.a, 123);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reflect_map() {
|
|
|
|
#[derive(Reflect, Hash)]
|
|
|
|
#[reflect(Hash)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
b: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
let key_a = Foo {
|
|
|
|
a: 1,
|
|
|
|
b: "k1".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let key_b = Foo {
|
|
|
|
a: 1,
|
|
|
|
b: "k1".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let key_c = Foo {
|
|
|
|
a: 3,
|
|
|
|
b: "k3".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut map = DynamicMap::default();
|
|
|
|
map.insert(key_a, 10u32);
|
|
|
|
assert_eq!(10, *map.get(&key_b).unwrap().downcast_ref::<u32>().unwrap());
|
|
|
|
assert!(map.get(&key_c).is_none());
|
|
|
|
*map.get_mut(&key_b).unwrap().downcast_mut::<u32>().unwrap() = 20;
|
|
|
|
assert_eq!(20, *map.get(&key_b).unwrap().downcast_ref::<u32>().unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-02-22 08:42:19 +00:00
|
|
|
#[allow(clippy::blacklisted_name)]
|
2020-11-28 00:39:59 +00:00
|
|
|
fn reflect_unit_struct() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo(u32, u64);
|
|
|
|
|
|
|
|
let mut foo = Foo(1, 2);
|
|
|
|
assert_eq!(1, *foo.get_field::<u32>(0).unwrap());
|
|
|
|
assert_eq!(2, *foo.get_field::<u64>(1).unwrap());
|
|
|
|
|
|
|
|
let mut patch = DynamicTupleStruct::default();
|
|
|
|
patch.insert(3u32);
|
|
|
|
patch.insert(4u64);
|
|
|
|
assert_eq!(3, *patch.field(0).unwrap().downcast_ref::<u32>().unwrap());
|
|
|
|
assert_eq!(4, *patch.field(1).unwrap().downcast_ref::<u64>().unwrap());
|
|
|
|
|
|
|
|
foo.apply(&patch);
|
|
|
|
assert_eq!(3, foo.0);
|
|
|
|
assert_eq!(4, foo.1);
|
|
|
|
|
|
|
|
let mut iter = patch.iter_fields();
|
|
|
|
assert_eq!(3, *iter.next().unwrap().downcast_ref::<u32>().unwrap());
|
|
|
|
assert_eq!(4, *iter.next().unwrap().downcast_ref::<u64>().unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "the given key does not support hashing")]
|
|
|
|
fn reflect_map_no_hash() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let foo = Foo { a: 1 };
|
|
|
|
|
|
|
|
let mut map = DynamicMap::default();
|
|
|
|
map.insert(foo, 10u32);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reflect_ignore() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
#[reflect(ignore)]
|
|
|
|
_b: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let foo = Foo { a: 1, _b: 2 };
|
|
|
|
|
|
|
|
let values: Vec<u32> = foo
|
|
|
|
.iter_fields()
|
|
|
|
.map(|value| *value.downcast_ref::<u32>().unwrap())
|
|
|
|
.collect();
|
|
|
|
assert_eq!(values, vec![1]);
|
|
|
|
}
|
|
|
|
|
bevy_reflect: Add `#[reflect(default)]` attribute for `FromReflect` (#4140)
# Objective
Currently, `FromReflect` makes a couple assumptions:
* Ignored fields must implement `Default`
* Active fields must implement `FromReflect`
* The reflected must be fully populated for active fields (can't use an empty `DynamicStruct`)
However, one or both of these requirements might be unachievable, such as for external types. In these cases, it might be nice to tell `FromReflect` to use a custom default.
## Solution
Added the `#[reflect(default)]` derive helper attribute. This attribute can be applied to any field (ignored or not) and will allow a default value to be specified in place of the regular `from_reflect()` call.
It takes two forms: `#[reflect(default)]` and `#[reflect(default = "some_func")]`. The former specifies that `Default::default()` should be used while the latter specifies that `some_func()` should be used. This is pretty much [how serde does it](https://serde.rs/field-attrs.html#default).
### Example
```rust
#[derive(Reflect, FromReflect)]
struct MyStruct {
// Use `Default::default()`
#[reflect(default)]
foo: String,
// Use `get_bar_default()`
#[reflect(default = "get_bar_default")]
#[reflect(ignore)]
bar: usize,
}
fn get_bar_default() -> usize {
123
}
```
### Active Fields
As an added benefit, this also allows active fields to be completely missing from their dynamic object. This is because the attribute tells `FromReflect` how to handle missing active fields (it still tries to use `from_reflect` first so the `FromReflect` trait is still required).
```rust
let dyn_struct = DynamicStruct::default();
// We can do this without actually including the active fields since they have `#[reflect(default)]`
let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct);
```
### Container Defaults
Also, with the addition of #3733, people will likely start adding `#[reflect(Default)]` to their types now. Just like with the fields, we can use this to mark the entire container as "defaultable". This grants us the ability to completely remove the field markers altogether if our type implements `Default` (and we're okay with fields using that instead of their own `Default` impls):
```rust
#[derive(Reflect, FromReflect)]
#[reflect(Default)]
struct MyStruct {
foo: String,
#[reflect(ignore)]
bar: usize,
}
impl Default for MyStruct {
fn default() -> Self {
Self {
foo: String::from("Hello"),
bar: 123,
}
}
}
// Again, we can now construct this from nothing pretty much
let dyn_struct = DynamicStruct::default();
let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct);
```
Now if _any_ field is missing when using `FromReflect`, we simply fallback onto the container's `Default` implementation.
This behavior can be completely overridden on a per-field basis, of course, by simply defining those same field attributes like before.
### Related
* #3733
* #1395
* #2377
---
## Changelog
* Added `#[reflect(default)]` field attribute for `FromReflect`
* Allows missing fields to be given a default value when using `FromReflect`
* `#[reflect(default)]` - Use the field's `Default` implementation
* `#[reflect(default = "some_fn")]` - Use a custom function to get the default value
* Allow `#[reflect(Default)]` to have a secondary usage as a container attribute
* Allows missing fields to be given a default value based on the container's `Default` impl when using `FromReflect`
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2022-05-30 19:06:25 +00:00
|
|
|
#[test]
|
|
|
|
fn from_reflect_should_use_default_field_attributes() {
|
|
|
|
#[derive(Reflect, FromReflect, Eq, PartialEq, Debug)]
|
|
|
|
struct MyStruct {
|
|
|
|
// Use `Default::default()`
|
|
|
|
// Note that this isn't an ignored field
|
|
|
|
#[reflect(default)]
|
|
|
|
foo: String,
|
|
|
|
|
|
|
|
// Use `get_bar_default()`
|
|
|
|
#[reflect(default = "get_bar_default")]
|
|
|
|
#[reflect(ignore)]
|
|
|
|
bar: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_bar_default() -> usize {
|
|
|
|
123
|
|
|
|
}
|
|
|
|
|
|
|
|
let expected = MyStruct {
|
|
|
|
foo: String::default(),
|
|
|
|
bar: 123,
|
|
|
|
};
|
|
|
|
|
|
|
|
let dyn_struct = DynamicStruct::default();
|
|
|
|
let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct);
|
|
|
|
|
|
|
|
assert_eq!(Some(expected), my_struct);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn from_reflect_should_use_default_container_attribute() {
|
|
|
|
#[derive(Reflect, FromReflect, Eq, PartialEq, Debug)]
|
|
|
|
#[reflect(Default)]
|
|
|
|
struct MyStruct {
|
|
|
|
foo: String,
|
|
|
|
#[reflect(ignore)]
|
|
|
|
bar: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for MyStruct {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
foo: String::from("Hello"),
|
|
|
|
bar: 123,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let expected = MyStruct {
|
|
|
|
foo: String::from("Hello"),
|
|
|
|
bar: 123,
|
|
|
|
};
|
|
|
|
|
|
|
|
let dyn_struct = DynamicStruct::default();
|
|
|
|
let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct);
|
|
|
|
|
|
|
|
assert_eq!(Some(expected), my_struct);
|
|
|
|
}
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
#[test]
|
|
|
|
fn reflect_complex_patch() {
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
#[derive(Reflect, Eq, PartialEq, Debug, FromReflect)]
|
Reflection cleanup (#1536)
This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed.
Supersedes #1533 and resolves #1528.
---
I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here):
- Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive.
- Same as above for `struct` and `#[reflect(...)]`, respectively.
- If a `struct` is used as a component, it should also have `#[reflect(Component)]`
- All reflected types should be registered in their plugins
I treated the following as components (added `#[reflect(Component)]` if necessary):
- `bevy_render`
- `struct RenderLayers`
- `bevy_transform`
- `struct GlobalTransform`
- `struct Parent`
- `struct Transform`
- `bevy_ui`
- `struct Style`
Not treated as components:
- `bevy_math`
- `struct Size<T>`
- `struct Rect<T>`
- Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin.
- `bevy_render`
- `struct Color`
- `struct PipelineSpecialization`
- `struct ShaderSpecialization`
- `enum PrimitiveTopology`
- `enum IndexFormat`
Not Addressed:
- I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR.
- I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
|
|
|
#[reflect(PartialEq)]
|
2020-11-28 00:39:59 +00:00
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
#[reflect(ignore)]
|
|
|
|
_b: u32,
|
|
|
|
c: Vec<isize>,
|
|
|
|
d: HashMap<usize, i8>,
|
|
|
|
e: Bar,
|
2021-01-08 03:50:09 +00:00
|
|
|
f: (i32, Vec<isize>, Bar),
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
g: Vec<(Baz, HashMap<usize, Bar>)>,
|
2022-05-13 01:13:30 +00:00
|
|
|
h: [u32; 2],
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
#[derive(Reflect, Eq, PartialEq, Clone, Debug, FromReflect)]
|
Reflection cleanup (#1536)
This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed.
Supersedes #1533 and resolves #1528.
---
I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here):
- Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive.
- Same as above for `struct` and `#[reflect(...)]`, respectively.
- If a `struct` is used as a component, it should also have `#[reflect(Component)]`
- All reflected types should be registered in their plugins
I treated the following as components (added `#[reflect(Component)]` if necessary):
- `bevy_render`
- `struct RenderLayers`
- `bevy_transform`
- `struct GlobalTransform`
- `struct Parent`
- `struct Transform`
- `bevy_ui`
- `struct Style`
Not treated as components:
- `bevy_math`
- `struct Size<T>`
- `struct Rect<T>`
- Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin.
- `bevy_render`
- `struct Color`
- `struct PipelineSpecialization`
- `struct ShaderSpecialization`
- `enum PrimitiveTopology`
- `enum IndexFormat`
Not Addressed:
- I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR.
- I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
|
|
|
#[reflect(PartialEq)]
|
2020-11-28 00:39:59 +00:00
|
|
|
struct Bar {
|
|
|
|
x: u32,
|
|
|
|
}
|
|
|
|
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
#[derive(Reflect, Eq, PartialEq, Debug, FromReflect)]
|
|
|
|
struct Baz(String);
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
let mut hash_map = HashMap::default();
|
|
|
|
hash_map.insert(1, 1);
|
|
|
|
hash_map.insert(2, 2);
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
|
|
|
|
let mut hash_map_baz = HashMap::default();
|
|
|
|
hash_map_baz.insert(1, Bar { x: 0 });
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
let mut foo = Foo {
|
|
|
|
a: 1,
|
|
|
|
_b: 1,
|
|
|
|
c: vec![1, 2],
|
|
|
|
d: hash_map,
|
|
|
|
e: Bar { x: 1 },
|
2021-01-08 03:50:09 +00:00
|
|
|
f: (1, vec![1, 2], Bar { x: 1 }),
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
g: vec![(Baz("string".to_string()), hash_map_baz)],
|
2022-05-13 01:13:30 +00:00
|
|
|
h: [2; 2],
|
2020-11-28 00:39:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut foo_patch = DynamicStruct::default();
|
|
|
|
foo_patch.insert("a", 2u32);
|
|
|
|
foo_patch.insert("b", 2u32); // this should be ignored
|
|
|
|
|
|
|
|
let mut list = DynamicList::default();
|
|
|
|
list.push(3isize);
|
|
|
|
list.push(4isize);
|
|
|
|
list.push(5isize);
|
2022-05-13 01:13:30 +00:00
|
|
|
foo_patch.insert("c", List::clone_dynamic(&list));
|
2020-11-28 00:39:59 +00:00
|
|
|
|
|
|
|
let mut map = DynamicMap::default();
|
|
|
|
map.insert(2usize, 3i8);
|
2022-07-04 13:04:19 +00:00
|
|
|
map.insert(3usize, 4i8);
|
2020-11-28 00:39:59 +00:00
|
|
|
foo_patch.insert("d", map);
|
|
|
|
|
|
|
|
let mut bar_patch = DynamicStruct::default();
|
|
|
|
bar_patch.insert("x", 2u32);
|
2021-01-08 03:50:09 +00:00
|
|
|
foo_patch.insert("e", bar_patch.clone_dynamic());
|
|
|
|
|
|
|
|
let mut tuple = DynamicTuple::default();
|
|
|
|
tuple.insert(2i32);
|
|
|
|
tuple.insert(list);
|
|
|
|
tuple.insert(bar_patch);
|
|
|
|
foo_patch.insert("f", tuple);
|
2020-11-28 00:39:59 +00:00
|
|
|
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
let mut composite = DynamicList::default();
|
|
|
|
composite.push({
|
|
|
|
let mut tuple = DynamicTuple::default();
|
|
|
|
tuple.insert({
|
|
|
|
let mut tuple_struct = DynamicTupleStruct::default();
|
|
|
|
tuple_struct.insert("new_string".to_string());
|
|
|
|
tuple_struct
|
|
|
|
});
|
|
|
|
tuple.insert({
|
|
|
|
let mut map = DynamicMap::default();
|
|
|
|
map.insert(1usize, {
|
|
|
|
let mut struct_ = DynamicStruct::default();
|
|
|
|
struct_.insert("x", 7u32);
|
|
|
|
struct_
|
|
|
|
});
|
|
|
|
map
|
|
|
|
});
|
|
|
|
tuple
|
|
|
|
});
|
|
|
|
foo_patch.insert("g", composite);
|
|
|
|
|
2022-05-13 01:13:30 +00:00
|
|
|
let array = DynamicArray::from_vec(vec![2u32, 2u32]);
|
|
|
|
foo_patch.insert("h", array);
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
foo.apply(&foo_patch);
|
|
|
|
|
|
|
|
let mut hash_map = HashMap::default();
|
|
|
|
hash_map.insert(1, 1);
|
|
|
|
hash_map.insert(2, 3);
|
2022-07-04 13:04:19 +00:00
|
|
|
hash_map.insert(3, 4);
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
|
|
|
|
let mut hash_map_baz = HashMap::default();
|
|
|
|
hash_map_baz.insert(1, Bar { x: 7 });
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
let expected_foo = Foo {
|
|
|
|
a: 2,
|
|
|
|
_b: 1,
|
|
|
|
c: vec![3, 4, 5],
|
|
|
|
d: hash_map,
|
|
|
|
e: Bar { x: 2 },
|
2021-01-08 03:50:09 +00:00
|
|
|
f: (2, vec![3, 4, 5], Bar { x: 2 }),
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
g: vec![(Baz("new_string".to_string()), hash_map_baz.clone())],
|
2022-05-13 01:13:30 +00:00
|
|
|
h: [2; 2],
|
2020-11-28 00:39:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(foo, expected_foo);
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
|
|
|
|
let new_foo = Foo::from_reflect(&foo_patch)
|
|
|
|
.expect("error while creating a concrete type from a dynamic type");
|
|
|
|
|
|
|
|
let mut hash_map = HashMap::default();
|
|
|
|
hash_map.insert(2, 3);
|
2022-07-04 13:04:19 +00:00
|
|
|
hash_map.insert(3, 4);
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
|
|
|
|
let expected_new_foo = Foo {
|
|
|
|
a: 2,
|
|
|
|
_b: 0,
|
|
|
|
c: vec![3, 4, 5],
|
|
|
|
d: hash_map,
|
|
|
|
e: Bar { x: 2 },
|
|
|
|
f: (2, vec![3, 4, 5], Bar { x: 2 }),
|
|
|
|
g: vec![(Baz("new_string".to_string()), hash_map_baz)],
|
2022-05-13 01:13:30 +00:00
|
|
|
h: [2; 2],
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(new_foo, expected_new_foo);
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reflect_serialize() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
#[reflect(ignore)]
|
|
|
|
_b: u32,
|
|
|
|
c: Vec<isize>,
|
|
|
|
d: HashMap<usize, i8>,
|
|
|
|
e: Bar,
|
|
|
|
f: String,
|
2021-01-08 03:50:09 +00:00
|
|
|
g: (i32, Vec<isize>, Bar),
|
2022-05-13 01:13:30 +00:00
|
|
|
h: [u32; 2],
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Bar {
|
|
|
|
x: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut hash_map = HashMap::default();
|
|
|
|
hash_map.insert(1, 1);
|
|
|
|
hash_map.insert(2, 2);
|
|
|
|
let foo = Foo {
|
|
|
|
a: 1,
|
|
|
|
_b: 1,
|
|
|
|
c: vec![1, 2],
|
|
|
|
d: hash_map,
|
|
|
|
e: Bar { x: 1 },
|
|
|
|
f: "hi".to_string(),
|
2021-01-08 03:50:09 +00:00
|
|
|
g: (1, vec![1, 2], Bar { x: 1 }),
|
2022-05-13 01:13:30 +00:00
|
|
|
h: [2; 2],
|
2020-11-28 00:39:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut registry = TypeRegistry::default();
|
|
|
|
registry.register::<u32>();
|
|
|
|
registry.register::<isize>();
|
|
|
|
registry.register::<usize>();
|
|
|
|
registry.register::<Bar>();
|
|
|
|
registry.register::<String>();
|
|
|
|
registry.register::<i8>();
|
2021-01-08 03:50:09 +00:00
|
|
|
registry.register::<i32>();
|
2020-11-28 00:39:59 +00:00
|
|
|
|
|
|
|
let serializer = ReflectSerializer::new(&foo, ®istry);
|
|
|
|
let serialized = to_string_pretty(&serializer, PrettyConfig::default()).unwrap();
|
|
|
|
|
|
|
|
let mut deserializer = Deserializer::from_str(&serialized).unwrap();
|
|
|
|
let reflect_deserializer = ReflectDeserializer::new(®istry);
|
|
|
|
let value = reflect_deserializer.deserialize(&mut deserializer).unwrap();
|
|
|
|
let dynamic_struct = value.take::<DynamicStruct>().unwrap();
|
|
|
|
|
2020-12-01 19:15:07 +00:00
|
|
|
assert!(foo.reflect_partial_eq(&dynamic_struct).unwrap());
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
Make `Reflect` safe to implement (#5010)
# Objective
Currently, `Reflect` is unsafe to implement because of a contract in which `any` and `any_mut` must return `self`, or `downcast` will cause UB. This PR makes `Reflect` safe, makes `downcast` not use unsafe, and eliminates this contract.
## Solution
This PR adds a method to `Reflect`, `any`. It also renames the old `any` to `as_any`.
`any` now takes a `Box<Self>` and returns a `Box<dyn Any>`.
---
## Changelog
### Added:
- `any()` method
- `represents()` method
### Changed:
- `Reflect` is now a safe trait
- `downcast()` is now safe
- The old `any` is now called `as_any`, and `any_mut` is now `as_mut_any`
## Migration Guide
- Reflect derives should not have to change anything
- Manual reflect impls will need to remove the `unsafe` keyword, add `any()` implementations, and rename the old `any` and `any_mut` to `as_any` and `as_mut_any`.
- Calls to `any`/`any_mut` must be changed to `as_any`/`as_mut_any`
## Points of discussion:
- Should renaming `any` be avoided and instead name the new method `any_box`?
- ~~Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.~~
- ~~Could/should `is` and `type_id()` be implemented differently? For example, moving `is` onto `Reflect` as an `fn(&self, TypeId) -> bool`~~
Co-authored-by: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com>
2022-06-27 16:52:25 +00:00
|
|
|
#[test]
|
|
|
|
fn reflect_downcast() {
|
|
|
|
#[derive(Reflect, Clone, Debug, PartialEq)]
|
|
|
|
struct Bar {
|
|
|
|
y: u8,
|
|
|
|
z: ::glam::Mat4,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Reflect, Clone, Debug, PartialEq)]
|
|
|
|
struct Foo {
|
|
|
|
x: i32,
|
|
|
|
s: String,
|
|
|
|
b: Bar,
|
|
|
|
u: usize,
|
|
|
|
t: (Vec3, String),
|
|
|
|
}
|
|
|
|
|
|
|
|
let foo = Foo {
|
|
|
|
x: 123,
|
|
|
|
s: "String".to_string(),
|
|
|
|
b: Bar {
|
|
|
|
y: 255,
|
|
|
|
z: ::glam::Mat4::from_cols_array(&[
|
|
|
|
0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0,
|
|
|
|
15.0,
|
|
|
|
]),
|
|
|
|
},
|
|
|
|
u: 1111111111111,
|
|
|
|
t: (Vec3::new(3.0, 2.0, 1.0), "Tuple String".to_string()),
|
|
|
|
};
|
|
|
|
|
|
|
|
let foo2: Box<dyn Reflect> = Box::new(foo.clone());
|
|
|
|
|
|
|
|
assert_eq!(foo, *foo2.downcast::<Foo>().unwrap());
|
|
|
|
}
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
#[test]
|
|
|
|
fn reflect_take() {
|
|
|
|
#[derive(Reflect, Debug, PartialEq)]
|
Reflection cleanup (#1536)
This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed.
Supersedes #1533 and resolves #1528.
---
I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here):
- Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive.
- Same as above for `struct` and `#[reflect(...)]`, respectively.
- If a `struct` is used as a component, it should also have `#[reflect(Component)]`
- All reflected types should be registered in their plugins
I treated the following as components (added `#[reflect(Component)]` if necessary):
- `bevy_render`
- `struct RenderLayers`
- `bevy_transform`
- `struct GlobalTransform`
- `struct Parent`
- `struct Transform`
- `bevy_ui`
- `struct Style`
Not treated as components:
- `bevy_math`
- `struct Size<T>`
- `struct Rect<T>`
- Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin.
- `bevy_render`
- `struct Color`
- `struct PipelineSpecialization`
- `struct ShaderSpecialization`
- `enum PrimitiveTopology`
- `enum IndexFormat`
Not Addressed:
- I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR.
- I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
|
|
|
#[reflect(PartialEq)]
|
2020-11-28 00:39:59 +00:00
|
|
|
struct Bar {
|
|
|
|
x: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let x: Box<dyn Reflect> = Box::new(Bar { x: 2 });
|
|
|
|
let y = x.take::<Bar>().unwrap();
|
|
|
|
assert_eq!(y, Bar { x: 2 });
|
|
|
|
}
|
2021-02-02 21:57:26 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn dynamic_names() {
|
|
|
|
let list = Vec::<usize>::new();
|
2022-05-13 01:13:30 +00:00
|
|
|
let dyn_list = List::clone_dynamic(&list);
|
2021-02-02 21:57:26 +00:00
|
|
|
assert_eq!(dyn_list.type_name(), std::any::type_name::<Vec<usize>>());
|
|
|
|
|
2022-05-13 01:13:30 +00:00
|
|
|
let array = [b'0'; 4];
|
|
|
|
let dyn_array = Array::clone_dynamic(&array);
|
|
|
|
assert_eq!(dyn_array.type_name(), std::any::type_name::<[u8; 4]>());
|
|
|
|
|
2021-02-02 21:57:26 +00:00
|
|
|
let map = HashMap::<usize, String>::default();
|
|
|
|
let dyn_map = map.clone_dynamic();
|
|
|
|
assert_eq!(
|
|
|
|
dyn_map.type_name(),
|
|
|
|
std::any::type_name::<HashMap<usize, String>>()
|
|
|
|
);
|
|
|
|
|
|
|
|
let tuple = (0usize, "1".to_string(), 2.0f32);
|
|
|
|
let mut dyn_tuple = tuple.clone_dynamic();
|
|
|
|
dyn_tuple.insert::<usize>(3);
|
|
|
|
assert_eq!(
|
|
|
|
dyn_tuple.type_name(),
|
|
|
|
std::any::type_name::<(usize, String, f32, usize)>()
|
|
|
|
);
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct TestStruct {
|
|
|
|
a: usize,
|
|
|
|
}
|
|
|
|
let struct_ = TestStruct { a: 0 };
|
|
|
|
let dyn_struct = struct_.clone_dynamic();
|
|
|
|
assert_eq!(dyn_struct.type_name(), std::any::type_name::<TestStruct>());
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct TestTupleStruct(usize);
|
|
|
|
let tuple_struct = TestTupleStruct(0);
|
|
|
|
let dyn_tuple_struct = tuple_struct.clone_dynamic();
|
|
|
|
assert_eq!(
|
|
|
|
dyn_tuple_struct.type_name(),
|
|
|
|
std::any::type_name::<TestTupleStruct>()
|
|
|
|
);
|
|
|
|
}
|
2022-04-25 13:54:48 +00:00
|
|
|
|
bevy_reflect: Add statically available type info for reflected types (#4042)
# Objective
> Resolves #4504
It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance.
## Solution
Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait:
```rust
pub trait Typed: Reflect {
fn type_info() -> &'static TypeInfo;
}
```
> Note: This trait was made separate from `Reflect` due to `Sized` restrictions.
If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically.
If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered).
### Usage
Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information.
```rust
#[derive(Reflect)]
struct MyTupleStruct(usize, i32, MyStruct);
let info = MyTupleStruct::type_info();
if let TypeInfo::TupleStruct(info) = info {
assert!(info.is::<MyTupleStruct>());
assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name());
assert!(info.field_at(1).unwrap().is::<i32>());
} else {
panic!("Expected `TypeInfo::TupleStruct`");
}
```
### Manual Implementations
It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls):
```rust
use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField};
use bevy_reflect::utility::TypeInfoCell;
struct Foo<T: Reflect>(T);
impl<T: Reflect> Typed for Foo<T> {
fn type_info() -> &'static TypeInfo {
static CELL: TypeInfoCell = TypeInfoCell::generic();
CELL.get_or_insert::<Self, _>(|| {
let fields = [UnnamedField::new::<T>()];
let info = TupleStructInfo::new::<Self>(&fields);
TypeInfo::TupleStruct(info)
})
}
}
```
## Benefits
One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like:
```rust
#[derive(Reflect)]
struct MyType {
foo: usize,
bar: Vec<String>
}
// RON to be deserialized:
(
type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object
value: {
// "foo" is a value type matching "usize"
"foo": 123,
// "bar" is a list type matching "Vec<String>" with item type "String"
"bar": ["a", "b", "c"]
}
)
```
Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data).
Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry.
## Discussion
Some items to discuss:
1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way?
2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically).
4. General usefulness of this change, including missing/unnecessary parts.
5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.)
## Compile Tests
I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR (aa5178e7736a6f8252e10e543e52722107649d3f) and main (c309acd4322b1c3b2089e247a2d28b938eb7b56d).
<details>
<summary>See More</summary>
The test project included 250 of the following structs (as well as a few other structs):
```rust
#[derive(Default)]
#[cfg_attr(feature = "reflect", derive(Reflect))]
#[cfg_attr(feature = "from_reflect", derive(FromReflect))]
pub struct Big001 {
inventory: Inventory,
foo: usize,
bar: String,
baz: ItemDescriptor,
items: [Item; 20],
hello: Option<String>,
world: HashMap<i32, String>,
okay: (isize, usize, /* wesize */),
nope: ((String, String), (f32, f32)),
blah: Cow<'static, str>,
}
```
> I don't know if the compiler can optimize all these duplicate structs away, but I think it's fine either way. We're comparing times, not finding the absolute worst-case time.
I only ran each build 3 times using `cargo build --timings` (thank you @devil-ira), each of which were preceeded by a `cargo clean --package bevy_reflect_compile_test`.
Here are the times I got:
| Test | Test 1 | Test 2 | Test 3 | Average |
| -------------------------------- | ------ | ------ | ------ | ------- |
| Main | 1.7s | 3.1s | 1.9s | 2.33s |
| Main + `Reflect` | 8.3s | 8.6s | 8.1s | 8.33s |
| Main + `Reflect` + `FromReflect` | 11.6s | 11.8s | 13.8s | 12.4s |
| PR | 3.5s | 1.8s | 1.9s | 2.4s |
| PR + `Reflect` | 9.2s | 8.8s | 9.3s | 9.1s |
| PR + `Reflect` + `FromReflect` | 12.9s | 12.3s | 12.5s | 12.56s |
</details>
---
## Future Work
Even though everything could probably be made `const`, we unfortunately can't. This is because `TypeId::of::<T>()` is not yet `const` (see https://github.com/rust-lang/rust/issues/77125). When it does get stabilized, it would probably be worth coming back and making things `const`.
Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-06-09 21:18:15 +00:00
|
|
|
#[test]
|
|
|
|
fn reflect_type_info() {
|
|
|
|
// TypeInfo
|
|
|
|
let info = i32::type_info();
|
|
|
|
assert_eq!(std::any::type_name::<i32>(), info.type_name());
|
|
|
|
assert_eq!(std::any::TypeId::of::<i32>(), info.type_id());
|
|
|
|
|
|
|
|
// TypeInfo (unsized)
|
|
|
|
assert_eq!(
|
|
|
|
std::any::TypeId::of::<dyn Reflect>(),
|
|
|
|
<dyn Reflect as Typed>::type_info().type_id()
|
|
|
|
);
|
|
|
|
|
|
|
|
// TypeInfo (instance)
|
|
|
|
let value: &dyn Reflect = &123_i32;
|
|
|
|
let info = value.get_type_info();
|
|
|
|
assert!(info.is::<i32>());
|
|
|
|
|
|
|
|
// Struct
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct MyStruct {
|
|
|
|
foo: i32,
|
|
|
|
bar: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
let info = MyStruct::type_info();
|
|
|
|
if let TypeInfo::Struct(info) = info {
|
|
|
|
assert!(info.is::<MyStruct>());
|
|
|
|
assert_eq!(std::any::type_name::<MyStruct>(), info.type_name());
|
|
|
|
assert_eq!(
|
|
|
|
std::any::type_name::<i32>(),
|
|
|
|
info.field("foo").unwrap().type_name()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
std::any::TypeId::of::<i32>(),
|
|
|
|
info.field("foo").unwrap().type_id()
|
|
|
|
);
|
|
|
|
assert!(info.field("foo").unwrap().is::<i32>());
|
|
|
|
assert_eq!("foo", info.field("foo").unwrap().name());
|
|
|
|
assert_eq!(
|
|
|
|
std::any::type_name::<usize>(),
|
|
|
|
info.field_at(1).unwrap().type_name()
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
panic!("Expected `TypeInfo::Struct`");
|
|
|
|
}
|
|
|
|
|
|
|
|
let value: &dyn Reflect = &MyStruct { foo: 123, bar: 321 };
|
|
|
|
let info = value.get_type_info();
|
|
|
|
assert!(info.is::<MyStruct>());
|
|
|
|
|
|
|
|
// Struct (generic)
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct MyGenericStruct<T: Reflect> {
|
|
|
|
foo: T,
|
|
|
|
bar: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
let info = <MyGenericStruct<i32>>::type_info();
|
|
|
|
if let TypeInfo::Struct(info) = info {
|
|
|
|
assert!(info.is::<MyGenericStruct<i32>>());
|
|
|
|
assert_eq!(
|
|
|
|
std::any::type_name::<MyGenericStruct<i32>>(),
|
|
|
|
info.type_name()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
std::any::type_name::<i32>(),
|
|
|
|
info.field("foo").unwrap().type_name()
|
|
|
|
);
|
|
|
|
assert_eq!("foo", info.field("foo").unwrap().name());
|
|
|
|
assert_eq!(
|
|
|
|
std::any::type_name::<usize>(),
|
|
|
|
info.field_at(1).unwrap().type_name()
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
panic!("Expected `TypeInfo::Struct`");
|
|
|
|
}
|
|
|
|
|
|
|
|
let value: &dyn Reflect = &MyGenericStruct {
|
|
|
|
foo: String::from("Hello!"),
|
|
|
|
bar: 321,
|
|
|
|
};
|
|
|
|
let info = value.get_type_info();
|
|
|
|
assert!(info.is::<MyGenericStruct<String>>());
|
|
|
|
|
|
|
|
// Tuple Struct
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct MyTupleStruct(usize, i32, MyStruct);
|
|
|
|
|
|
|
|
let info = MyTupleStruct::type_info();
|
|
|
|
if let TypeInfo::TupleStruct(info) = info {
|
|
|
|
assert!(info.is::<MyTupleStruct>());
|
|
|
|
assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name());
|
|
|
|
assert_eq!(
|
|
|
|
std::any::type_name::<i32>(),
|
|
|
|
info.field_at(1).unwrap().type_name()
|
|
|
|
);
|
|
|
|
assert!(info.field_at(1).unwrap().is::<i32>());
|
|
|
|
} else {
|
|
|
|
panic!("Expected `TypeInfo::TupleStruct`");
|
|
|
|
}
|
|
|
|
|
|
|
|
let value: &dyn Reflect = &MyTupleStruct(123, 321, MyStruct { foo: 123, bar: 321 });
|
|
|
|
let info = value.get_type_info();
|
|
|
|
assert!(info.is::<MyTupleStruct>());
|
|
|
|
|
|
|
|
// Tuple
|
|
|
|
type MyTuple = (u32, f32, String);
|
|
|
|
|
|
|
|
let info = MyTuple::type_info();
|
|
|
|
if let TypeInfo::Tuple(info) = info {
|
|
|
|
assert!(info.is::<MyTuple>());
|
|
|
|
assert_eq!(std::any::type_name::<MyTuple>(), info.type_name());
|
|
|
|
assert_eq!(
|
|
|
|
std::any::type_name::<f32>(),
|
|
|
|
info.field_at(1).unwrap().type_name()
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
panic!("Expected `TypeInfo::Tuple`");
|
|
|
|
}
|
|
|
|
|
|
|
|
let value: &dyn Reflect = &(123_u32, 1.23_f32, String::from("Hello!"));
|
|
|
|
let info = value.get_type_info();
|
|
|
|
assert!(info.is::<MyTuple>());
|
|
|
|
|
|
|
|
// List
|
|
|
|
type MyList = Vec<usize>;
|
|
|
|
|
|
|
|
let info = MyList::type_info();
|
|
|
|
if let TypeInfo::List(info) = info {
|
|
|
|
assert!(info.is::<MyList>());
|
|
|
|
assert!(info.item_is::<usize>());
|
|
|
|
assert_eq!(std::any::type_name::<MyList>(), info.type_name());
|
|
|
|
assert_eq!(std::any::type_name::<usize>(), info.item_type_name());
|
|
|
|
} else {
|
|
|
|
panic!("Expected `TypeInfo::List`");
|
|
|
|
}
|
|
|
|
|
|
|
|
let value: &dyn Reflect = &vec![123_usize];
|
|
|
|
let info = value.get_type_info();
|
|
|
|
assert!(info.is::<MyList>());
|
|
|
|
|
|
|
|
// List (SmallVec)
|
|
|
|
#[cfg(feature = "smallvec")]
|
|
|
|
{
|
|
|
|
type MySmallVec = smallvec::SmallVec<[String; 2]>;
|
|
|
|
|
|
|
|
let info = MySmallVec::type_info();
|
|
|
|
if let TypeInfo::List(info) = info {
|
|
|
|
assert!(info.is::<MySmallVec>());
|
|
|
|
assert!(info.item_is::<String>());
|
|
|
|
assert_eq!(std::any::type_name::<MySmallVec>(), info.type_name());
|
|
|
|
assert_eq!(std::any::type_name::<String>(), info.item_type_name());
|
|
|
|
} else {
|
|
|
|
panic!("Expected `TypeInfo::List`");
|
|
|
|
}
|
|
|
|
|
|
|
|
let value: MySmallVec = smallvec::smallvec![String::default(); 2];
|
|
|
|
let value: &dyn Reflect = &value;
|
|
|
|
let info = value.get_type_info();
|
|
|
|
assert!(info.is::<MySmallVec>());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Array
|
|
|
|
type MyArray = [usize; 3];
|
|
|
|
|
|
|
|
let info = MyArray::type_info();
|
|
|
|
if let TypeInfo::Array(info) = info {
|
|
|
|
assert!(info.is::<MyArray>());
|
|
|
|
assert!(info.item_is::<usize>());
|
|
|
|
assert_eq!(std::any::type_name::<MyArray>(), info.type_name());
|
|
|
|
assert_eq!(std::any::type_name::<usize>(), info.item_type_name());
|
|
|
|
assert_eq!(3, info.capacity());
|
|
|
|
} else {
|
|
|
|
panic!("Expected `TypeInfo::Array`");
|
|
|
|
}
|
|
|
|
|
|
|
|
let value: &dyn Reflect = &[1usize, 2usize, 3usize];
|
|
|
|
let info = value.get_type_info();
|
|
|
|
assert!(info.is::<MyArray>());
|
|
|
|
|
|
|
|
// Map
|
|
|
|
type MyMap = HashMap<usize, f32>;
|
|
|
|
|
|
|
|
let info = MyMap::type_info();
|
|
|
|
if let TypeInfo::Map(info) = info {
|
|
|
|
assert!(info.is::<MyMap>());
|
|
|
|
assert!(info.key_is::<usize>());
|
|
|
|
assert!(info.value_is::<f32>());
|
|
|
|
assert_eq!(std::any::type_name::<MyMap>(), info.type_name());
|
|
|
|
assert_eq!(std::any::type_name::<usize>(), info.key_type_name());
|
|
|
|
assert_eq!(std::any::type_name::<f32>(), info.value_type_name());
|
|
|
|
} else {
|
|
|
|
panic!("Expected `TypeInfo::Map`");
|
|
|
|
}
|
|
|
|
|
|
|
|
let value: &dyn Reflect = &MyMap::new();
|
|
|
|
let info = value.get_type_info();
|
|
|
|
assert!(info.is::<MyMap>());
|
|
|
|
|
|
|
|
// Value
|
|
|
|
type MyValue = String;
|
|
|
|
|
|
|
|
let info = MyValue::type_info();
|
|
|
|
if let TypeInfo::Value(info) = info {
|
|
|
|
assert!(info.is::<MyValue>());
|
|
|
|
assert_eq!(std::any::type_name::<MyValue>(), info.type_name());
|
|
|
|
} else {
|
|
|
|
panic!("Expected `TypeInfo::Value`");
|
|
|
|
}
|
|
|
|
|
|
|
|
let value: &dyn Reflect = &String::from("Hello!");
|
|
|
|
let info = value.get_type_info();
|
|
|
|
assert!(info.is::<MyValue>());
|
|
|
|
|
|
|
|
// Dynamic
|
|
|
|
type MyDynamic = DynamicList;
|
|
|
|
|
|
|
|
let info = MyDynamic::type_info();
|
|
|
|
if let TypeInfo::Dynamic(info) = info {
|
|
|
|
assert!(info.is::<MyDynamic>());
|
|
|
|
assert_eq!(std::any::type_name::<MyDynamic>(), info.type_name());
|
|
|
|
} else {
|
|
|
|
panic!("Expected `TypeInfo::Dynamic`");
|
|
|
|
}
|
|
|
|
|
|
|
|
let value: &dyn Reflect = &DynamicList::default();
|
|
|
|
let info = value.get_type_info();
|
|
|
|
assert!(info.is::<MyDynamic>());
|
|
|
|
}
|
|
|
|
|
2022-04-25 13:54:48 +00:00
|
|
|
#[test]
|
|
|
|
fn as_reflect() {
|
|
|
|
trait TestTrait: Reflect {}
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct TestStruct;
|
|
|
|
|
|
|
|
impl TestTrait for TestStruct {}
|
|
|
|
|
|
|
|
let trait_object: Box<dyn TestTrait> = Box::new(TestStruct);
|
|
|
|
|
|
|
|
// Should compile:
|
|
|
|
let _ = trait_object.as_reflect();
|
|
|
|
}
|
2022-05-09 16:32:15 +00:00
|
|
|
|
bevy_reflect: Improve debug formatting for reflected types (#4218)
# Objective
Debugging reflected types can be somewhat frustrating since all `dyn Reflect` trait objects return something like `Reflect(core::option::Option<alloc::string::String>)`.
It would be much nicer to be able to see the actual value— or even use a custom `Debug` implementation.
## Solution
Added `Reflect::debug` which allows users to customize the debug output. It sets defaults for all `ReflectRef` subtraits and falls back to `Reflect(type_name)` if no `Debug` implementation was registered.
To register a custom `Debug` impl, users can add `#[reflect(Debug)]` like they can with other traits.
### Example
Using the following structs:
```rust
#[derive(Reflect)]
pub struct Foo {
a: usize,
nested: Bar,
#[reflect(ignore)]
_ignored: NonReflectedValue,
}
#[derive(Reflect)]
pub struct Bar {
value: Vec2,
tuple_value: (i32, String),
list_value: Vec<usize>,
// We can't determine debug formatting for Option<T> yet
unknown_value: Option<String>,
custom_debug: CustomDebug
}
#[derive(Reflect)]
#[reflect(Debug)]
struct CustomDebug;
impl Debug for CustomDebug {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "This is a custom debug!")
}
}
pub struct NonReflectedValue {
_a: usize,
}
```
We can do:
```rust
let value = Foo {
a: 1,
_ignored: NonReflectedValue { _a: 10 },
nested: Bar {
value: Vec2::new(1.23, 3.21),
tuple_value: (123, String::from("Hello")),
list_value: vec![1, 2, 3],
unknown_value: Some(String::from("World")),
custom_debug: CustomDebug
},
};
let reflected_value: &dyn Reflect = &value;
println!("{:#?}", reflected_value)
```
Which results in:
```rust
Foo {
a: 2,
nested: Bar {
value: Vec2(
1.23,
3.21,
),
tuple_value: (
123,
"Hello",
),
list_value: [
1,
2,
3,
],
unknown_value: Reflect(core::option::Option<alloc::string::String>),
custom_debug: This is a custom debug!,
},
}
```
Notice that neither `Foo` nor `Bar` implement `Debug`, yet we can still deduce it. This might be a concern if we're worried about leaking internal values. If it is, we might want to consider a way to exclude fields (possibly with a `#[reflect(hide)]` macro) or make it purely opt in (as opposed to the default implementation automatically handled by ReflectRef subtraits).
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2022-05-30 16:41:31 +00:00
|
|
|
#[test]
|
|
|
|
fn should_reflect_debug() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Test {
|
|
|
|
value: usize,
|
|
|
|
list: Vec<String>,
|
|
|
|
array: [f32; 3],
|
|
|
|
map: HashMap<i32, f32>,
|
|
|
|
a_struct: SomeStruct,
|
|
|
|
a_tuple_struct: SomeTupleStruct,
|
|
|
|
custom: CustomDebug,
|
|
|
|
unknown: Option<String>,
|
|
|
|
#[reflect(ignore)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
ignored: isize,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct SomeStruct {
|
|
|
|
foo: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct SomeTupleStruct(String);
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
#[reflect(Debug)]
|
|
|
|
struct CustomDebug;
|
|
|
|
impl Debug for CustomDebug {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.write_str("Cool debug!")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut map = HashMap::new();
|
|
|
|
map.insert(123, 1.23);
|
|
|
|
|
|
|
|
let test = Test {
|
|
|
|
value: 123,
|
|
|
|
list: vec![String::from("A"), String::from("B"), String::from("C")],
|
|
|
|
array: [1.0, 2.0, 3.0],
|
|
|
|
map,
|
|
|
|
a_struct: SomeStruct {
|
|
|
|
foo: String::from("A Struct!"),
|
|
|
|
},
|
|
|
|
a_tuple_struct: SomeTupleStruct(String::from("A Tuple Struct!")),
|
|
|
|
custom: CustomDebug,
|
|
|
|
unknown: Some(String::from("Enums aren't supported yet :(")),
|
|
|
|
ignored: 321,
|
|
|
|
};
|
|
|
|
|
|
|
|
let reflected: &dyn Reflect = &test;
|
|
|
|
let expected = r#"
|
|
|
|
bevy_reflect::tests::should_reflect_debug::Test {
|
|
|
|
value: 123,
|
|
|
|
list: [
|
|
|
|
"A",
|
|
|
|
"B",
|
|
|
|
"C",
|
|
|
|
],
|
|
|
|
array: [
|
|
|
|
1.0,
|
|
|
|
2.0,
|
|
|
|
3.0,
|
|
|
|
],
|
|
|
|
map: {
|
|
|
|
123: 1.23,
|
|
|
|
},
|
|
|
|
a_struct: bevy_reflect::tests::should_reflect_debug::SomeStruct {
|
|
|
|
foo: "A Struct!",
|
|
|
|
},
|
|
|
|
a_tuple_struct: bevy_reflect::tests::should_reflect_debug::SomeTupleStruct(
|
|
|
|
"A Tuple Struct!",
|
|
|
|
),
|
|
|
|
custom: Cool debug!,
|
|
|
|
unknown: Reflect(core::option::Option<alloc::string::String>),
|
|
|
|
}"#;
|
|
|
|
|
|
|
|
assert_eq!(expected, format!("\n{:#?}", reflected));
|
|
|
|
}
|
|
|
|
|
2022-05-09 16:32:15 +00:00
|
|
|
#[cfg(feature = "glam")]
|
|
|
|
mod glam {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn vec3_serialization() {
|
|
|
|
let v = vec3(12.0, 3.0, -6.9);
|
|
|
|
|
|
|
|
let mut registry = TypeRegistry::default();
|
2022-06-20 17:18:58 +00:00
|
|
|
registry.register::<f32>();
|
|
|
|
registry.register::<Vec3>();
|
2022-05-09 16:32:15 +00:00
|
|
|
|
|
|
|
let ser = ReflectSerializer::new(&v, ®istry);
|
|
|
|
|
2022-06-15 19:18:53 +00:00
|
|
|
let result = ron::to_string(&ser).expect("Failed to serialize to string");
|
2022-05-09 16:32:15 +00:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
result,
|
2022-07-03 19:55:33 +00:00
|
|
|
r#"{"type":"glam::f32::vec3::Vec3","struct":{"x":{"type":"f32","value":12.0},"y":{"type":"f32","value":3.0},"z":{"type":"f32","value":-6.9}}}"#
|
2022-05-09 16:32:15 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn vec3_deserialization() {
|
|
|
|
let data = r#"{"type":"glam::vec3::Vec3","struct":{"x":{"type":"f32","value":12},"y":{"type":"f32","value":3},"z":{"type":"f32","value":-6.9}}}"#;
|
|
|
|
|
|
|
|
let mut registry = TypeRegistry::default();
|
|
|
|
registry.add_registration(Vec3::get_type_registration());
|
|
|
|
registry.add_registration(f32::get_type_registration());
|
|
|
|
|
|
|
|
let de = ReflectDeserializer::new(®istry);
|
|
|
|
|
|
|
|
let mut deserializer =
|
|
|
|
ron::de::Deserializer::from_str(data).expect("Failed to acquire deserializer");
|
|
|
|
|
|
|
|
let dynamic_struct = de
|
|
|
|
.deserialize(&mut deserializer)
|
|
|
|
.expect("Failed to deserialize");
|
|
|
|
|
|
|
|
let mut result = Vec3::default();
|
|
|
|
|
|
|
|
result.apply(&*dynamic_struct);
|
|
|
|
|
|
|
|
assert_eq!(result, vec3(12.0, 3.0, -6.9));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn vec3_field_access() {
|
|
|
|
let mut v = vec3(1.0, 2.0, 3.0);
|
|
|
|
|
|
|
|
assert_eq!(*v.get_field::<f32>("x").unwrap(), 1.0);
|
|
|
|
|
|
|
|
*v.get_field_mut::<f32>("y").unwrap() = 6.0;
|
|
|
|
|
|
|
|
assert_eq!(v.y, 6.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn vec3_path_access() {
|
|
|
|
let mut v = vec3(1.0, 2.0, 3.0);
|
|
|
|
|
|
|
|
assert_eq!(*v.path("x").unwrap().downcast_ref::<f32>().unwrap(), 1.0);
|
|
|
|
|
|
|
|
*v.path_mut("y").unwrap().downcast_mut::<f32>().unwrap() = 6.0;
|
|
|
|
|
|
|
|
assert_eq!(v.y, 6.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn vec3_apply_dynamic() {
|
|
|
|
let mut v = vec3(3.0, 3.0, 3.0);
|
|
|
|
|
|
|
|
let mut d = DynamicStruct::default();
|
|
|
|
d.insert("x", 4.0f32);
|
|
|
|
d.insert("y", 2.0f32);
|
|
|
|
d.insert("z", 1.0f32);
|
|
|
|
|
|
|
|
v.apply(&d);
|
|
|
|
|
|
|
|
assert_eq!(v, vec3(4.0, 2.0, 1.0));
|
|
|
|
}
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|