2020-11-28 00:39:59 +00:00
|
|
|
[package]
|
|
|
|
name = "bevy_reflect"
|
2023-07-10 21:19:27 +00:00
|
|
|
version = "0.12.0-dev"
|
2021-10-27 00:12:14 +00:00
|
|
|
edition = "2021"
|
2020-11-28 00:39:59 +00:00
|
|
|
description = "Dynamically interact with rust types"
|
|
|
|
homepage = "https://bevyengine.org"
|
|
|
|
repository = "https://github.com/bevyengine/bevy"
|
2021-07-23 21:11:51 +00:00
|
|
|
license = "MIT OR Apache-2.0"
|
2020-11-28 00:39:59 +00:00
|
|
|
keywords = ["bevy"]
|
|
|
|
readme = "README.md"
|
|
|
|
|
|
|
|
[features]
|
2022-10-18 13:49:57 +00:00
|
|
|
default = []
|
2023-06-08 20:33:21 +00:00
|
|
|
# When enabled, provides Bevy-related reflection implementations
|
|
|
|
bevy = ["glam", "smallvec", "bevy_math", "smol_str"]
|
2022-10-18 13:49:57 +00:00
|
|
|
# When enabled, allows documentation comments to be accessed via reflection
|
|
|
|
documentation = ["bevy_reflect_derive/documentation"]
|
2020-11-28 00:39:59 +00:00
|
|
|
|
|
|
|
[dependencies]
|
|
|
|
# bevy
|
2023-07-10 21:19:27 +00:00
|
|
|
bevy_math = { path = "../bevy_math", version = "0.12.0-dev", features = [
|
2023-06-08 20:33:21 +00:00
|
|
|
"serialize",
|
|
|
|
], optional = true }
|
2023-07-10 21:19:27 +00:00
|
|
|
bevy_reflect_derive = { path = "bevy_reflect_derive", version = "0.12.0-dev" }
|
|
|
|
bevy_utils = { path = "../bevy_utils", version = "0.12.0-dev" }
|
|
|
|
bevy_ptr = { path = "../bevy_ptr", version = "0.12.0-dev" }
|
2020-11-28 00:39:59 +00:00
|
|
|
|
|
|
|
# other
|
|
|
|
erased-serde = "0.3"
|
|
|
|
downcast-rs = "1.2"
|
2022-07-14 21:17:16 +00:00
|
|
|
parking_lot = "0.12.1"
|
2020-11-28 00:39:59 +00:00
|
|
|
thiserror = "1.0"
|
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
|
|
|
once_cell = "1.11"
|
2023-01-09 21:57:14 +00:00
|
|
|
serde = "1"
|
2023-06-08 20:33:21 +00:00
|
|
|
smallvec = { version = "1.6", features = [
|
|
|
|
"serde",
|
|
|
|
"union",
|
|
|
|
"const_generics",
|
|
|
|
], optional = true }
|
2023-08-31 12:55:17 +00:00
|
|
|
glam = { version = "0.24.1", features = ["serde"], optional = true }
|
2023-06-08 20:33:21 +00:00
|
|
|
smol_str = { version = "0.2.0", optional = true }
|
2020-11-28 00:39:59 +00:00
|
|
|
|
|
|
|
[dev-dependencies]
|
2022-09-02 14:20:49 +00:00
|
|
|
ron = "0.8.0"
|
bevy_reflect: Binary formats (#6140)
# Objective
Closes #5934
Currently it is not possible to de/serialize data to non-self-describing formats using reflection.
## Solution
Add support for non-self-describing de/serialization using reflection.
This allows us to use binary formatters, like [`postcard`](https://crates.io/crates/postcard):
```rust
#[derive(Reflect, FromReflect, Debug, PartialEq)]
struct Foo {
data: String
}
let mut registry = TypeRegistry::new();
registry.register::<Foo>();
let input = Foo {
data: "Hello world!".to_string()
};
// === Serialize! === //
let serializer = ReflectSerializer::new(&input, ®istry);
let bytes: Vec<u8> = postcard::to_allocvec(&serializer).unwrap();
println!("{:?}", bytes); // Output: [129, 217, 61, 98, ...]
// === Deserialize! === //
let deserializer = UntypedReflectDeserializer::new(®istry);
let dynamic_output = deserializer
.deserialize(&mut postcard::Deserializer::from_bytes(&bytes))
.unwrap();
let output = <Foo as FromReflect>::from_reflect(dynamic_output.as_ref()).unwrap();
assert_eq!(expected, output); // OK!
```
#### Crates Tested
- ~~[`rmp-serde`](https://crates.io/crates/rmp-serde)~~ Apparently, this _is_ self-describing
- ~~[`bincode` v2.0.0-rc.1](https://crates.io/crates/bincode/2.0.0-rc.1) (using [this PR](https://github.com/bincode-org/bincode/pull/586))~~ This actually works for the latest release (v1.3.3) of [`bincode`](https://crates.io/crates/bincode) as well. You just need to be sure to use fixed-int encoding.
- [`postcard`](https://crates.io/crates/postcard)
## Future Work
Ideally, we would refactor the `serde` module, but I don't think I'll do that in this PR so as to keep the diff relatively small (and to avoid any painful rebases). This should probably be done once this is merged, though.
Some areas we could improve with a refactor:
* Split deserialization logic across multiple files
* Consolidate helper functions/structs
* Make the logic more DRY
---
## Changelog
- Add support for non-self-describing de/serialization using reflection.
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2022-11-04 02:22:54 +00:00
|
|
|
rmp-serde = "1.1"
|
|
|
|
bincode = "1.3"
|
2022-10-18 13:49:57 +00:00
|
|
|
|
|
|
|
[[example]]
|
|
|
|
name = "reflect_docs"
|
|
|
|
path = "examples/reflect_docs.rs"
|
|
|
|
required-features = ["documentation"]
|