From c8f41731f506182b2b745c16e52b8f77c4ad014e Mon Sep 17 00:00:00 2001 From: oddfacade Date: Wed, 21 Sep 2022 19:21:41 -0400 Subject: [PATCH] allow `FromType` to be specialized if implemented, `SpecializedFromType` will be used instead of `FromType` in the context of the `GetTypeRegistration` macro implementation. see the new test in `crates/bevy_reflect/src/lib.rs` for a demonstration. --- .../bevy_reflect_derive/Cargo.toml | 2 +- .../bevy_reflect_derive/src/registration.rs | 32 ++++++++++- crates/bevy_reflect/src/impls/glam.rs | 1 + crates/bevy_reflect/src/impls/rect.rs | 1 + crates/bevy_reflect/src/impls/std.rs | 4 +- crates/bevy_reflect/src/lib.rs | 54 +++++++++++++++++++ crates/bevy_reflect/src/type_data.rs | 35 ++++++++++++ crates/bevy_reflect/src/type_registry.rs | 27 +--------- 8 files changed, 127 insertions(+), 29 deletions(-) create mode 100644 crates/bevy_reflect/src/type_data.rs diff --git a/crates/bevy_reflect/bevy_reflect_derive/Cargo.toml b/crates/bevy_reflect/bevy_reflect_derive/Cargo.toml index c7c947537fec7..e3fccb8429c37 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/Cargo.toml +++ b/crates/bevy_reflect/bevy_reflect_derive/Cargo.toml @@ -18,4 +18,4 @@ syn = { version = "1.0", features = ["full"] } proc-macro2 = "1.0" quote = "1.0" uuid = { version = "1.1", features = ["v4"] } -bit-set = "0.5.2" \ No newline at end of file +bit-set = "0.5.2" diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index 0f234105452fe..cda1138fcc9cb 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -26,10 +26,40 @@ pub(crate) fn impl_get_type_registration( #[allow(unused_mut)] impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause { fn get_type_registration() -> #bevy_reflect_path::TypeRegistration { + struct FromTypeCollector(std::marker::PhantomData, std::marker::PhantomData); + impl> FromTypeCollector { + #[inline] + fn new() -> Self { + Self(std::marker::PhantomData, std::marker::PhantomData) + } + + #[inline] + fn collect(&self) -> T { + T::from_type() + } + } + + trait CollectSpecialized { + type Data; + fn collect(self) -> Self::Data; + } + + impl CollectSpecialized for FromTypeCollector + where + T: #bevy_reflect_path::SpecializedFromType, + { + type Data = T; + + #[inline] + fn collect(self) -> Self::Data { + T::specialized_from_type() + } + } + let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>(); registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type()); #serialization_data - #(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)* + #(registration.insert::<#registration_data>(FromTypeCollector::<#type_name #ty_generics, #registration_data>::new().collect());)* registration } } diff --git a/crates/bevy_reflect/src/impls/glam.rs b/crates/bevy_reflect/src/impls/glam.rs index 9dfcc8293ce23..82d5f6727deec 100644 --- a/crates/bevy_reflect/src/impls/glam.rs +++ b/crates/bevy_reflect/src/impls/glam.rs @@ -1,3 +1,4 @@ +#[cfg(not(doctest))] use crate as bevy_reflect; use crate::prelude::ReflectDefault; use crate::reflect::Reflect; diff --git a/crates/bevy_reflect/src/impls/rect.rs b/crates/bevy_reflect/src/impls/rect.rs index f134e9cfe0627..7460ece1405f4 100644 --- a/crates/bevy_reflect/src/impls/rect.rs +++ b/crates/bevy_reflect/src/impls/rect.rs @@ -1,3 +1,4 @@ +#[cfg(not(doctest))] use crate as bevy_reflect; use crate::prelude::ReflectDefault; use crate::reflect::Reflect; diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 1d7acbabf096a..fc13154acf1f7 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,4 +1,6 @@ -use crate::{self as bevy_reflect, ReflectFromPtr}; +#[cfg(not(doctest))] +use crate as bevy_reflect; +use crate::ReflectFromPtr; use crate::{ map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum, DynamicMap, Enum, EnumInfo, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index a9e57d3fbfac0..e0d18a259b22d 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -9,6 +9,7 @@ mod reflect; mod struct_trait; mod tuple; mod tuple_struct; +mod type_data; mod type_info; mod type_registry; mod type_uuid; @@ -55,6 +56,7 @@ pub use reflect::*; pub use struct_trait::*; pub use tuple::*; pub use tuple_struct::*; +pub use type_data::*; pub use type_info::*; pub use type_registry::*; pub use type_uuid::*; @@ -1045,4 +1047,56 @@ bevy_reflect::tests::should_reflect_debug::Test { assert_eq!(v, vec3(4.0, 2.0, 1.0)); } } + + #[test] + fn type_data_specialization() { + #[derive(Reflect, Debug)] + #[reflect(MyTrait)] + struct HasGenericTypeData; + + #[derive(Reflect, Debug)] + #[reflect(MyTrait)] + struct HasSpecializedTypeData; + + #[derive(Clone, Debug, PartialEq, Eq)] + enum ReflectMyTrait { + Generic, + Specialized, + } + + impl FromType for ReflectMyTrait { + fn from_type() -> Self { + Self::Generic + } + } + + impl SpecializedFromType for ReflectMyTrait { + fn specialized_from_type() -> Self { + Self::Specialized + } + } + + let type_registry = { + let mut type_registry = TypeRegistry::new(); + type_registry.register::(); + type_registry.register::(); + type_registry + }; + + assert_eq!( + type_registry + .get_type_data::(std::any::TypeId::of::()) + .expect("Generic type data is missing!"), + &ReflectMyTrait::Generic, + "Got specialized type data instead of generic type data." + ); + + assert_eq!( + type_registry + .get_type_data::(std::any::TypeId::of::()) + .expect("Specialized type data is missing!"), + &ReflectMyTrait::Specialized, + "Got generic type data instead of specialized type data." + ); + } } diff --git a/crates/bevy_reflect/src/type_data.rs b/crates/bevy_reflect/src/type_data.rs new file mode 100644 index 0000000000000..88e4d69e33b7b --- /dev/null +++ b/crates/bevy_reflect/src/type_data.rs @@ -0,0 +1,35 @@ +use downcast_rs::{impl_downcast, Downcast}; + +/// A trait for types generated by the [`#[reflect_trait]`][0] attribute macro. +/// +/// [0]: crate::reflect_trait +pub trait TypeData: Downcast + Send + Sync { + fn clone_type_data(&self) -> Box; +} +impl_downcast!(TypeData); + +impl TypeData for T +where + T: Clone, +{ + fn clone_type_data(&self) -> Box { + Box::new(self.clone()) + } +} + +/// Trait used to generate [`TypeData`] for trait reflection. +/// +/// This is used by the `#[derive(Reflect)]` macro to generate an implementation +/// of [`TypeData`] to pass to [`crate::TypeRegistration::insert`]. +pub trait FromType { + fn from_type() -> Self; +} + +/// A trait which can be used to specialize a blanket `FromType` implementation. +/// +/// If implemented, the `#[derive(Reflect)]` macro will use this trait's +/// `specialized_from_type` method instead of `FromType::::from_type()` to +/// generate a corresponding [`TypeData`] for `T`. +pub trait SpecializedFromType: FromType { + fn specialized_from_type() -> Self; +} diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index fe3302774b761..bf0dc18b7c91b 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -1,7 +1,6 @@ -use crate::{serde::Serializable, Reflect, TypeInfo, Typed}; +use crate::{serde::Serializable, FromType, Reflect, TypeData, TypeInfo, Typed}; use bevy_ptr::{Ptr, PtrMut}; use bevy_utils::{HashMap, HashSet}; -use downcast_rs::{impl_downcast, Downcast}; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use serde::Deserialize; use std::{any::TypeId, fmt::Debug, sync::Arc}; @@ -361,30 +360,6 @@ impl Clone for TypeRegistration { } } } -/// A trait for types generated by the [`#[reflect_trait]`][0] attribute macro. -/// -/// [0]: crate::reflect_trait -pub trait TypeData: Downcast + Send + Sync { - fn clone_type_data(&self) -> Box; -} -impl_downcast!(TypeData); - -impl TypeData for T -where - T: Clone, -{ - fn clone_type_data(&self) -> Box { - Box::new(self.clone()) - } -} - -/// Trait used to generate [`TypeData`] for trait reflection. -/// -/// This is used by the `#[derive(Reflect)]` macro to generate an implementation -/// of [`TypeData`] to pass to [`TypeRegistration::insert`]. -pub trait FromType { - fn from_type() -> Self; -} /// A struct used to serialize reflected instances of a type. ///