Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - bevy_reflect: ReflectFromPtr to create &dyn Reflect from a *const () #4475

Closed
Closed
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/bevy_reflect/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ bevy = ["glam", "smallvec"]
# bevy
bevy_reflect_derive = { path = "bevy_reflect_derive", version = "0.8.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" }
bevy_ptr = { path = "../bevy_ptr", version = "0.8.0-dev" }

# other
erased-serde = "0.3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub(crate) fn impl_get_type_registration(
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause {
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
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());
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)*
registration
}
Expand Down
5 changes: 4 additions & 1 deletion crates/bevy_reflect/src/impls/std.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate as bevy_reflect;
use crate::{self as bevy_reflect, ReflectFromPtr};
use crate::{
map_partial_eq, serde::Serializable, Array, ArrayIter, DynamicMap, FromReflect, FromType,
GetTypeRegistration, List, Map, MapIter, Reflect, ReflectDeserialize, ReflectMut, ReflectRef,
Expand Down Expand Up @@ -162,6 +162,7 @@ impl<T: FromReflect + for<'de> Deserialize<'de>> GetTypeRegistration for Vec<T>
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Vec<T>>();
registration.insert::<ReflectDeserialize>(FromType::<Vec<T>>::from_type());
registration.insert::<ReflectFromPtr>(FromType::<Vec<T>>::from_type());
registration
}
}
Expand Down Expand Up @@ -292,6 +293,7 @@ where
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Self>();
registration.insert::<ReflectDeserialize>(FromType::<Self>::from_type());
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
jakobhellermann marked this conversation as resolved.
Show resolved Hide resolved
registration
}
}
Expand Down Expand Up @@ -531,6 +533,7 @@ impl GetTypeRegistration for Cow<'static, str> {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Cow<'static, str>>();
registration.insert::<ReflectDeserialize>(FromType::<Cow<'static, str>>::from_type());
registration.insert::<ReflectFromPtr>(FromType::<Cow<'static, str>>::from_type());
registration
}
}
Expand Down
127 changes: 125 additions & 2 deletions crates/bevy_reflect/src/type_registry.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::Reflect;
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};
use std::{any::TypeId, fmt::Debug, ptr::NonNull, sync::Arc};

/// A registry of reflected types.
#[derive(Default)]
Expand Down Expand Up @@ -379,11 +380,133 @@ impl<T: for<'a> Deserialize<'a> + Reflect> FromType<T> for ReflectDeserialize {
}
}

/// [`Reflect`] values are commonly used in situations where the actual types of values
/// are not known at runtime. In such situations you might have access to a `*const ()` pointer
/// that you know implements [`Reflect`], but have no way of turning it into a `&dyn Reflect`.
///
/// This is where [`ReflectFromPtr`] comes in, when creating a [`ReflectFromPtr`] for a given type `T: Reflect`.
/// Internally, this saves a concrete function `*const T -> const dyn Reflect` which lets you create a trait object of [`Reflect`]
/// from a pointer.
///
/// # Example
/// ```rust
/// use bevy_reflect::{TypeRegistry, Reflect, ReflectFromPtr};
/// use bevy_ptr::Ptr;
/// use std::ptr::NonNull;
///
/// #[derive(Reflect)]
/// struct Reflected(String);
///
/// let mut type_registry = TypeRegistry::default();
/// type_registry.register::<Reflected>();
///
/// let value = Reflected("Hello world!".to_string());
///
/// let reflect_data = type_registry.get(std::any::TypeId::of::<Reflected>()).unwrap();
/// let reflect_from_ptr = reflect_data.data::<ReflectFromPtr>().unwrap();
/// // SAFE: `value` is of type `Reflected`, which the `ReflectFromPtr` was created for
/// let value = unsafe { reflect_from_ptr.as_reflect(&value) };
///
/// assert_eq!(value.downcast_ref::<Reflected>().unwrap().0, "Hello world!");
/// ```
#[derive(Clone)]
pub struct ReflectFromPtr {
jakobhellermann marked this conversation as resolved.
Show resolved Hide resolved
type_id: TypeId,
to_reflect: for<'a> unsafe fn(Ptr<'a>) -> &'a dyn Reflect,
to_reflect_mut: for<'a> unsafe fn(PtrMut<'a>) -> &'a mut dyn Reflect,
}

impl ReflectFromPtr {
/// Returns the [`TypeId`] that the [`ReflectFromPtr`] was constructed for
pub fn type_id(&self) -> TypeId {
self.type_id
}

/// # Safety
///
/// `val` must be a pointer to value of the type that the [`ReflectFromPtr`] was constructed for.
/// This can be verified by checking that the type id returned by [`ReflectFromPtr::type_id`] is the expected one.
pub unsafe fn as_reflect_ptr<'a>(&self, val: Ptr<'a>) -> &'a dyn Reflect {
(self.to_reflect)(val)
}

/// # Safety
///
/// `val` must be a pointer to a value of the type that the [`ReflectFromPtr`] was constructed for
/// This can be verified by checking that the type id returned by [`ReflectFromPtr::type_id`] is the expected one.
pub unsafe fn as_reflect_ptr_mut<'a>(&self, val: PtrMut<'a>) -> &'a mut dyn Reflect {
(self.to_reflect_mut)(val)
}

pub fn as_reflect<'a, T: Reflect>(&self, val: &'a T) -> &'a dyn Reflect {
jakobhellermann marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(self.type_id, std::any::TypeId::of::<T>());
// SAFE: `val` is of type `T`
unsafe { &*self.as_reflect_ptr(Ptr::new(NonNull::from(val).cast())) }
}

pub fn as_reflect_mut<'a, T: Reflect>(&self, val: &'a mut T) -> &'a mut dyn Reflect {
jakobhellermann marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(self.type_id, std::any::TypeId::of::<T>());
// SAFE: `val` is of type `T`
unsafe { &mut *self.as_reflect_ptr_mut(PtrMut::new(NonNull::from(val).cast())) }
}
}

impl<T: Reflect> FromType<T> for ReflectFromPtr {
fn from_type() -> Self {
ReflectFromPtr {
type_id: std::any::TypeId::of::<T>(),
to_reflect: |ptr| {
// SAFE: only called from `as_reflect`, where the `ptr` is guaranteed to be of type `T`,
// and `as_reflect_ptr`, where the caller promises to call it with type `T`
unsafe { ptr.deref::<T>() as &dyn Reflect }
},
to_reflect_mut: |ptr| {
// SAFE: only called from `as_reflect_mut`, where the `ptr` is guaranteed to be of type `T`,
// and `as_reflect_ptr_mut`, where the caller promises to call it with type `T`
unsafe { ptr.deref_mut::<T>() as &mut dyn Reflect }
},
}
}
}

#[cfg(test)]
mod test {
use crate::TypeRegistration;
use crate::{GetTypeRegistration, ReflectFromPtr, TypeRegistration};
use bevy_utils::HashMap;

use crate as bevy_reflect;
use crate::Reflect;

#[test]
fn test_reflect_from_ptr() {
#[derive(Reflect)]
struct Foo {
a: f32,
}

let foo_registration = <Foo as GetTypeRegistration>::get_type_registration();
let reflect_from_ptr = foo_registration.data::<ReflectFromPtr>().unwrap();

let mut value = Foo { a: 1.0 };

let dyn_reflect = reflect_from_ptr.as_reflect_mut(&mut value);
match dyn_reflect.reflect_mut() {
bevy_reflect::ReflectMut::Struct(strukt) => {
strukt.field_mut("a").unwrap().apply(&2.0f32)
}
_ => panic!("invalid reflection"),
}

let dyn_reflect = reflect_from_ptr.as_reflect(&value);
match dyn_reflect.reflect_ref() {
bevy_reflect::ReflectRef::Struct(strukt) => {
let a = strukt.field("a").unwrap().downcast_ref::<f32>().unwrap();
assert_eq!(*a, 2.0);
}
_ => panic!("invalid reflection"),
}
}

#[test]
fn test_get_short_name() {
assert_eq!(
Expand Down