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

Get Component's dyn Trait by ComponentId #9837

Open
wants to merge 28 commits into
base: main
Choose a base branch
from

Conversation

ycysdf
Copy link
Contributor

@ycysdf ycysdf commented Sep 18, 2023

Objective

  • Get Component's dyn Trait by ComponentId

Solution

  • Add TraitTypeData trait. This trait associates the trait object corresponding to TypeData

Changelog

  • Add TypeDataMapper trait. It map a type to a TraitTypeData. This trait is implemented automatically by #[reflect_trait]. It map dyn MyTrait maps to ReflectMyTrait.

Migration Guide

  • Methods get ,get_mut,get_boxed move onto the trait, Need to import the use bevy: : reflect: : TraitTypeData ;

@ycysdf ycysdf changed the title Get dyn component Get Component's dyn object by ComponentId Sep 18, 2023
use crate::prelude::{AppTypeRegistry, World};

/// An extension trait for World for reflection related functions
pub trait WorldExt {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like having these methods stored separately, but I don't really see the point of the trait extension method rather than just another impl block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I referred to ReflectCommandExt, and these methods depend on AppTypeRegistry, so I also defined a WorldExt, which I'm not sure is better

return None;
};

let type_registry = self.resource::<AppTypeRegistry>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a get_resource, with a ? operator again. No need to introduce panics.

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events A-Reflection Runtime information about types labels Sep 18, 2023
/// This trait can be implemented automatically by the `#[reflect_trait]` macro,
///
/// If you implement [`TypeData`] for your component yourself, you should also implement this trait if you need to dynamically get the dyn object
pub trait ReflectFnsTypeData: TypeData {
Copy link
Member

@MrGVSV MrGVSV Sep 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a huge fan of creating a new trait, but I understand why it might be useful.

However, if we decide to add this, I feel like the name should reflect the specific kind of type data we're dealing with here, which are reflected traits. (The other kind would be custom type data, which may represent traits but do not convert a dyn Reflect into a dyn Trait.)

Perhaps, we rename this to TraitTypeData? Or maybe even just TraitObject?

crates/bevy_reflect/src/type_registry.rs Outdated Show resolved Hide resolved
crates/bevy_reflect/src/type_registry.rs Outdated Show resolved Hide resolved
crates/bevy_reflect/src/type_registry.rs Outdated Show resolved Hide resolved
crates/bevy_reflect/src/type_registry.rs Outdated Show resolved Hide resolved
crates/bevy_reflect/src/type_registry.rs Outdated Show resolved Hide resolved
crates/bevy_reflect/src/type_registry.rs Outdated Show resolved Hide resolved
crates/bevy_ecs/src/reflect/world.rs Outdated Show resolved Hide resolved
@nicopap
Copy link
Contributor

nicopap commented Sep 19, 2023

Ping @JoJoJet because they made bevy-trait-query and they probably know a lot of useful things about this.

Copy link
Contributor

@nicopap nicopap left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer defer on JoJoJet for whether this should be added or not.

crates/bevy_ecs/src/reflect/world.rs Outdated Show resolved Hide resolved
///
/// If you implement [`TypeData`] for your component yourself, you should also implement this trait if you need to dynamically get the dyn object
pub trait ReflectFnsTypeData: TypeData {
type Dyn: ?Sized;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks when the TypeData doesn't have an associated trait. For example in https://lib.rs/crates/cuicui_reflect_query, Queryable is not a trait. It would stop compiling, and I don't think it's an isolated use-case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeData can choose not to implement this trait.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why I think renaming this trait would help clarify its intent

@alice-i-cecile
Copy link
Member

I cleaned up all the clearly resolved conversations for you :) @ycysdf, feel free to do that yourself in the future on your Bevy PRs as you submit fixes: it really helps reviewers understand what's outstanding.

@ycysdf
Copy link
Contributor Author

ycysdf commented Sep 19, 2023

@alice-i-cecile thank you

@ycysdf ycysdf changed the title Get Component's dyn object by ComponentId Get Component's dyn Trait by ComponentId Sep 19, 2023
Copy link
Member

@JoJoJet JoJoJet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am mostly unfamiliar with the bevy's reflection system, since I used my own registry in order to implement bevy-trait-query, which let me optimize single-item trait queries to be nearly as fast as concrete type queries. One benefit of the approach used in this PR is that is plays more nicely with reflection, since trait impls will be registered along with the type -- compare this to bevy-trait-query, where each trait impl currently needs to be registered separately, even when the type itself has already been registered. However, I don't know what the performance implications of doing it this way are -- since this PR does not attempt to implement queries, I take it that performance is not a priority here.

One syntactic concern I have is the use of auto-generated reflect types for traits. If I have

#[reflect_trait]
trait MyTrait {}

I would get a trait object by calling

world.get_dyn_mut_by_id::<ReflectMyTrait>(entity, component_id);

I believe it would be more intuitive if this instead looked like

world.get_dyn_mut_by_id::<dyn MyTrait>(entity, component_id);

Would this change be doable?

@ycysdf
Copy link
Contributor Author

ycysdf commented Sep 24, 2023

I am mostly unfamiliar with the bevy's reflection system, since I used my own registry in order to implement bevy-trait-query, which let me optimize single-item trait queries to be nearly as fast as concrete type queries. One benefit of the approach used in this PR is that is plays more nicely with reflection, since trait impls will be registered along with the type -- compare this to bevy-trait-query, where each trait impl currently needs to be registered separately, even when the type itself has already been registered. However, I don't know what the performance implications of doing it this way are -- since this PR does not attempt to implement queries, I take it that performance is not a priority here.

One syntactic concern I have is the use of auto-generated reflect types for traits. If I have

#[reflect_trait]
trait MyTrait {}

I would get a trait object by calling

world.get_dyn_mut_by_id::<ReflectMyTrait>(entity, component_id);

I believe it would be more intuitive if this instead looked like

world.get_dyn_mut_by_id::<dyn MyTrait>(entity, component_id);

Would this change be doable?

Good Idea ! I tried to implement it, but the solution needed to add a new Trait 'TraitTypeDataRelevance'

@ycysdf
Copy link
Contributor Author

ycysdf commented Sep 24, 2023

Now can get the dyn Trait from both

world.get_dyn_mut_by_id::<ReflectMyTrait>(entity, component_id);

and

world.get_dyn_mut_by_id::<dyn MyTrait>(entity, component_id);

@ycysdf
Copy link
Contributor Author

ycysdf commented Sep 24, 2023

@JoJoJet
I'm a bit of a newbie to Bevy

I think the trait query of 'bevy-trait query' is very good

Comment on lines 605 to 608
/// Associate a `TraitTypeData`
pub trait TraitTypeDataRelevance {
type TypeData: TraitTypeData;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably needs more documentation considering manual implementors may want to make use of it.

@@ -599,6 +602,39 @@ impl<T: Reflect> FromType<T> for ReflectFromPtr {
}
}

/// Associate a `TraitTypeData`
pub trait TraitTypeDataRelevance {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bikeshed: I think a name like AsoociatedTypeData, TypeDataAssoc, TypeDataMapper, etc. would be more indicative of how this is meant to be used.

Additionally, I think we can leave out the Trait part of the name since this doesn't necessarily need to be used for dyn Trait. For example, a user could associate a Player struct type with a ReflectPlayerSettings or something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rename to TyepDataMapper and update TypeDataMapper::TypeData bound to TypeData. I feel better about it.

}

/// Retrieves an immutable `dyn T` reference to the given entity's Component of the given [`ComponentId`]
pub fn get_dyn_by_id<T: TraitTypeDataRelevance + ?Sized>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to add the ?Sized bound as a supertrait of TraitTypeDataRelevance? Or does that not work/isn't desirable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't work unfortunately. The implicit Sized bound comes from the type parameter T, not from the trait. All traits can have ?Sized implementors, unless you explicitly add a Sized bound on the trait definition.

@alice-i-cecile alice-i-cecile added the C-Feature A new feature, making something new possible label Jan 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events A-Reflection Runtime information about types C-Feature A new feature, making something new possible
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants