Skip to content

Commit

Permalink
Implement Sub-App Labels (#2695)
Browse files Browse the repository at this point in the history
This is a rather simple but wide change, and it involves adding a new `bevy_app_macros` crate. Let me know if there is a better way to do any of this!

---

# Objective

- Allow adding and accessing sub-apps by using a label instead of an index

## Solution

- Migrate the bevy label implementation and derive code to the `bevy_utils` and `bevy_macro_utils` crates and then add a new `SubAppLabel` trait to the `bevy_app` crate that is used when adding or getting a sub-app from an app.
  • Loading branch information
zicklag committed Aug 24, 2021
1 parent c3d3ae7 commit e290a7e
Show file tree
Hide file tree
Showing 21 changed files with 244 additions and 168 deletions.
1 change: 1 addition & 0 deletions crates/bevy_app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ bevy_derive = { path = "../bevy_derive", version = "0.5.0" }
bevy_ecs = { path = "../bevy_ecs", version = "0.5.0" }
bevy_reflect = { path = "../bevy_reflect", version = "0.5.0", optional = true }
bevy_utils = { path = "../bevy_utils", version = "0.5.0" }
bevy_app_macros = { path = "./macros", version = "0.5.0" }

# other
serde = { version = "1.0", features = ["derive"], optional = true }
Expand Down
16 changes: 16 additions & 0 deletions crates/bevy_app/macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "bevy_app_macros"
version = "0.5.0"
description = "Bevy App Macros"
edition = "2018"
license = "MIT OR Apache-2.0"

[lib]
proc-macro = true

[dependencies]
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.5.0" }

syn = "1.0"
quote = "1.0"
proc-macro2 = "1.0"
19 changes: 19 additions & 0 deletions crates/bevy_app/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
extern crate proc_macro;

use bevy_macro_utils::{derive_label, BevyManifest};
use proc_macro::TokenStream;
use quote::format_ident;

#[proc_macro_derive(SubAppLabel)]
pub fn derive_sub_app_label(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let mut trait_path = bevy_app_path();
trait_path
.segments
.push(format_ident!("SubAppLabel").into());
derive_label(input, trait_path)
}

fn bevy_app_path() -> syn::Path {
BevyManifest::default().get_path("bevy_app")
}
33 changes: 22 additions & 11 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ use bevy_ecs::{
},
world::World,
};
use bevy_utils::tracing::debug;
use bevy_utils::{tracing::debug, HashMap};
use std::{fmt::Debug, hash::Hash};

pub use bevy_app_macros::SubAppLabel;

#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;

bevy_utils::define_label!(SubAppLabel);
type BoxedSubAppLabel = Box<dyn SubAppLabel>;

#[allow(clippy::needless_doctest_main)]
/// Containers of app logic and data
///
Expand Down Expand Up @@ -40,7 +45,7 @@ pub struct App {
pub world: World,
pub runner: Box<dyn Fn(App)>,
pub schedule: Schedule,
sub_apps: Vec<SubApp>,
sub_apps: HashMap<BoxedSubAppLabel, SubApp>,
}

struct SubApp {
Expand Down Expand Up @@ -77,7 +82,7 @@ impl App {
world: Default::default(),
schedule: Default::default(),
runner: Box::new(run_once),
sub_apps: Vec::new(),
sub_apps: HashMap::default(),
}
}

Expand All @@ -87,7 +92,7 @@ impl App {
#[cfg(feature = "trace")]
let _bevy_frame_update_guard = bevy_frame_update_span.enter();
self.schedule.run(&mut self.world);
for sub_app in self.sub_apps.iter_mut() {
for sub_app in self.sub_apps.values_mut() {
(sub_app.runner)(&mut self.world, &mut sub_app.app);
}
}
Expand Down Expand Up @@ -589,19 +594,25 @@ impl App {

pub fn add_sub_app(
&mut self,
label: impl SubAppLabel,
app: App,
f: impl Fn(&mut World, &mut App) + 'static,
) -> &mut Self {
self.sub_apps.push(SubApp {
app,
runner: Box::new(f),
});
self.sub_apps.insert(
Box::new(label),
SubApp {
app,
runner: Box::new(f),
},
);
self
}

// TODO: use labels instead of indices
pub fn sub_app_mut(&mut self, index: usize) -> &mut App {
&mut self.sub_apps[index].app
pub fn sub_app_mut(&mut self, label: impl SubAppLabel) -> Option<&mut App> {
let label = Box::new(label) as BoxedSubAppLabel;
self.sub_apps
.get_mut(&label)
.map(|sub_app| &mut sub_app.app)
}
}

Expand Down
4 changes: 0 additions & 4 deletions crates/bevy_ecs/macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
name = "bevy_ecs_macros"
version = "0.5.0"
description = "Bevy ECS Macros"
authors = [
"Bevy Contributors <bevyengine@gmail.com>",
"Carter Anderson <mcanders1@gmail.com>",
]
edition = "2018"
license = "MIT OR Apache-2.0"

Expand Down
53 changes: 25 additions & 28 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
extern crate proc_macro;

use bevy_macro_utils::BevyManifest;
use bevy_macro_utils::{derive_label, BevyManifest};
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use proc_macro2::Span;
use quote::{format_ident, quote};
use syn::{
parse::{Parse, ParseStream},
parse_macro_input,
punctuated::Punctuated,
token::Comma,
Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, Lifetime, LitInt,
Path, Result, Token,
Result, Token,
};

struct AllTuples {
Expand Down Expand Up @@ -436,46 +436,43 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
#[proc_macro_derive(SystemLabel)]
pub fn derive_system_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);

derive_label(input, Ident::new("SystemLabel", Span::call_site())).into()
let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into());
trait_path
.segments
.push(format_ident!("SystemLabel").into());
derive_label(input, trait_path)
}

#[proc_macro_derive(StageLabel)]
pub fn derive_stage_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
derive_label(input, Ident::new("StageLabel", Span::call_site())).into()
let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into());
trait_path.segments.push(format_ident!("StageLabel").into());
derive_label(input, trait_path)
}

#[proc_macro_derive(AmbiguitySetLabel)]
pub fn derive_ambiguity_set_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
derive_label(input, Ident::new("AmbiguitySetLabel", Span::call_site())).into()
let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into());
trait_path
.segments
.push(format_ident!("AmbiguitySetLabel").into());
derive_label(input, trait_path)
}

#[proc_macro_derive(RunCriteriaLabel)]
pub fn derive_run_criteria_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
derive_label(input, Ident::new("RunCriteriaLabel", Span::call_site())).into()
}

fn derive_label(input: DeriveInput, label_type: Ident) -> TokenStream2 {
let ident = input.ident;
let ecs_path: Path = bevy_ecs_path();

let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
where_token: Default::default(),
predicates: Default::default(),
});
where_clause.predicates.push(syn::parse2(quote! { Self: Eq + ::std::fmt::Debug + ::std::hash::Hash + Clone + Send + Sync + 'static }).unwrap());

quote! {
impl #impl_generics #ecs_path::schedule::#label_type for #ident #ty_generics #where_clause {
fn dyn_clone(&self) -> Box<dyn #ecs_path::schedule::#label_type> {
Box::new(Clone::clone(self))
}
}
}
let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into());
trait_path
.segments
.push(format_ident!("RunCriteriaLabel").into());
derive_label(input, trait_path)
}

fn bevy_ecs_path() -> syn::Path {
Expand Down
110 changes: 5 additions & 105 deletions crates/bevy_ecs/src/schedule/label.rs
Original file line number Diff line number Diff line change
@@ -1,115 +1,15 @@
pub use bevy_ecs_macros::{AmbiguitySetLabel, RunCriteriaLabel, StageLabel, SystemLabel};

use std::{
any::Any,
borrow::Cow,
fmt::Debug,
hash::{Hash, Hasher},
};
use bevy_utils::define_label;

pub trait DynEq: Any {
fn as_any(&self) -> &dyn Any;

fn dyn_eq(&self, other: &dyn DynEq) -> bool;
}

impl<T> DynEq for T
where
T: Any + Eq,
{
fn as_any(&self) -> &dyn Any {
self
}

fn dyn_eq(&self, other: &dyn DynEq) -> bool {
if let Some(other) = other.as_any().downcast_ref::<T>() {
return self == other;
}
false
}
}

pub trait DynHash: DynEq {
fn as_dyn_eq(&self) -> &dyn DynEq;

fn dyn_hash(&self, state: &mut dyn Hasher);
}

impl<T> DynHash for T
where
T: DynEq + Hash,
{
fn as_dyn_eq(&self) -> &dyn DynEq {
self
}

fn dyn_hash(&self, mut state: &mut dyn Hasher) {
T::hash(self, &mut state);
self.type_id().hash(&mut state);
}
}

pub trait StageLabel: DynHash + Debug + Send + Sync + 'static {
#[doc(hidden)]
fn dyn_clone(&self) -> Box<dyn StageLabel>;
}
define_label!(StageLabel);
pub(crate) type BoxedStageLabel = Box<dyn StageLabel>;

pub trait SystemLabel: DynHash + Debug + Send + Sync + 'static {
#[doc(hidden)]
fn dyn_clone(&self) -> Box<dyn SystemLabel>;
}
define_label!(SystemLabel);
pub(crate) type BoxedSystemLabel = Box<dyn SystemLabel>;

pub trait AmbiguitySetLabel: DynHash + Debug + Send + Sync + 'static {
#[doc(hidden)]
fn dyn_clone(&self) -> Box<dyn AmbiguitySetLabel>;
}
define_label!(AmbiguitySetLabel);
pub(crate) type BoxedAmbiguitySetLabel = Box<dyn AmbiguitySetLabel>;

pub trait RunCriteriaLabel: DynHash + Debug + Send + Sync + 'static {
#[doc(hidden)]
fn dyn_clone(&self) -> Box<dyn RunCriteriaLabel>;
}
define_label!(RunCriteriaLabel);
pub(crate) type BoxedRunCriteriaLabel = Box<dyn RunCriteriaLabel>;

macro_rules! impl_label {
($trait_name:ident) => {
impl PartialEq for dyn $trait_name {
fn eq(&self, other: &Self) -> bool {
self.dyn_eq(other.as_dyn_eq())
}
}

impl Eq for dyn $trait_name {}

impl Hash for dyn $trait_name {
fn hash<H: Hasher>(&self, state: &mut H) {
self.dyn_hash(state);
}
}

impl Clone for Box<dyn $trait_name> {
fn clone(&self) -> Self {
self.dyn_clone()
}
}

impl $trait_name for Cow<'static, str> {
fn dyn_clone(&self) -> Box<dyn $trait_name> {
Box::new(self.clone())
}
}

impl $trait_name for &'static str {
fn dyn_clone(&self) -> Box<dyn $trait_name> {
Box::new(<&str>::clone(self))
}
}
};
}

impl_label!(StageLabel);
impl_label!(SystemLabel);
impl_label!(AmbiguitySetLabel);
impl_label!(RunCriteriaLabel);
1 change: 1 addition & 0 deletions crates/bevy_macro_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ keywords = ["bevy"]
[dependencies]
cargo-manifest = "0.2.3"
syn = "1.0"
quote = "1.0"
27 changes: 27 additions & 0 deletions crates/bevy_macro_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ extern crate proc_macro;

use cargo_manifest::{DepsSet, Manifest};
use proc_macro::TokenStream;
use quote::quote;
use std::{env, path::PathBuf};

pub struct BevyManifest {
Expand Down Expand Up @@ -59,3 +60,29 @@ fn get_path(path: &str) -> syn::Path {
fn parse_str<T: syn::parse::Parse>(path: &str) -> T {
syn::parse(path.parse::<TokenStream>().unwrap()).unwrap()
}

/// Derive a label trait
///
/// # Args
///
/// - `input`: The [`syn::DeriveInput`] for struct that is deriving the label trait
/// - `trait_path`: The path [`syn::Path`] to the label trait
pub fn derive_label(input: syn::DeriveInput, trait_path: syn::Path) -> TokenStream {
let ident = input.ident;

let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
where_token: Default::default(),
predicates: Default::default(),
});
where_clause.predicates.push(syn::parse2(quote! { Self: Eq + ::std::fmt::Debug + ::std::hash::Hash + Clone + Send + Sync + 'static }).unwrap());

(quote! {
impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
fn dyn_clone(&self) -> Box<dyn #trait_path> {
Box::new(Clone::clone(self))
}
}
})
.into()
}
Loading

0 comments on commit e290a7e

Please sign in to comment.