Support SystemInput tuples up to 8 elements (#16814)
# Objective
- Writing an API, and I want to allow users to pass in extra data
alongside the API provided input, and tuples are the most natural
extension in this case.
- Bring `SystemInput` up to par with `SystemParam` for tuple support.
## Solution
- Added impls for tuples up to 8 elements. If you need a 9-arity tuple
or more, write your own `SystemInput` type (it's incredibly simple to
do).
## Testing
- Added a test demonstrating this.
---
## Showcase
Tuples of arbitrary`SystemInput`s are now supported:
```rust
fn by_value((In(a), In(b)): (In<usize>, In<usize>)) -> usize {
a + b
}
fn by_mut((InMut(a), In(b)): (InMut<usize>, In<usize>)) {
*a += b;
}
let mut world = World::new();
let mut by_value = IntoSystem::into_system(by_value);
let mut by_mut = IntoSystem::into_system(by_mut);
by_value.initialize(&mut world);
by_mut.initialize(&mut world);
assert_eq!(by_value.run((12, 24), &mut world), 36);
let mut a = 10;
let b = 5;
by_mut.run((&mut a, b), &mut world);
assert_eq!(*a, 15);
```
This commit is contained in:
parent
6178ce93e8
commit
c14135d150
@ -1,5 +1,7 @@
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
use variadics_please::all_tuples;
|
||||
|
||||
use crate::{bundle::Bundle, prelude::Trigger, system::System};
|
||||
|
||||
/// Trait for types that can be used as input to [`System`]s.
|
||||
@ -11,6 +13,28 @@ use crate::{bundle::Bundle, prelude::Trigger, system::System};
|
||||
/// - [`InMut<T>`]: For mutable references to values
|
||||
/// - [`Trigger<E, B>`]: For [`ObserverSystem`]s
|
||||
/// - [`StaticSystemInput<I>`]: For arbitrary [`SystemInput`]s in generic contexts
|
||||
/// - Tuples of [`SystemInput`]s up to 8 elements
|
||||
///
|
||||
/// For advanced usecases, you can implement this trait for your own types.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Tuples of [`SystemInput`]s
|
||||
///
|
||||
/// ```
|
||||
/// use bevy_ecs::prelude::*;
|
||||
///
|
||||
/// fn add((InMut(a), In(b)): (InMut<usize>, In<usize>)) {
|
||||
/// *a += b;
|
||||
/// }
|
||||
/// # let mut world = World::new();
|
||||
/// # let mut add = IntoSystem::into_system(add);
|
||||
/// # add.initialize(&mut world);
|
||||
/// # let mut a = 12;
|
||||
/// # let b = 24;
|
||||
/// # add.run((&mut a, b), &mut world);
|
||||
/// # assert_eq!(a, 36);
|
||||
/// ```
|
||||
///
|
||||
/// [`ObserverSystem`]: crate::system::ObserverSystem
|
||||
pub trait SystemInput: Sized {
|
||||
@ -31,14 +55,6 @@ pub trait SystemInput: Sized {
|
||||
/// Shorthand way to get the [`System::In`] for a [`System`] as a [`SystemInput::Inner`].
|
||||
pub type SystemIn<'a, S> = <<S as System>::In as SystemInput>::Inner<'a>;
|
||||
|
||||
/// [`SystemInput`] type for systems that take no input.
|
||||
impl SystemInput for () {
|
||||
type Param<'i> = ();
|
||||
type Inner<'i> = ();
|
||||
|
||||
fn wrap(_this: Self::Inner<'_>) -> Self::Param<'_> {}
|
||||
}
|
||||
|
||||
/// A [`SystemInput`] type which denotes that a [`System`] receives
|
||||
/// an input value of type `T` from its caller.
|
||||
///
|
||||
@ -47,6 +63,8 @@ impl SystemInput for () {
|
||||
/// with this `In` type, but only the first param of a function may be tagged as an input. This also
|
||||
/// means a system can only have one or zero input parameters.
|
||||
///
|
||||
/// See [`SystemInput`] to learn more about system inputs in general.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Here is a simple example of a system that takes a [`usize`] and returns the square of it.
|
||||
@ -99,6 +117,8 @@ impl<T> DerefMut for In<T> {
|
||||
/// This is similar to [`In`] but takes a reference to a value instead of the value itself.
|
||||
/// See [`InMut`] for the mutable version.
|
||||
///
|
||||
/// See [`SystemInput`] to learn more about system inputs in general.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Here is a simple example of a system that logs the passed in message.
|
||||
@ -150,6 +170,8 @@ impl<'i, T: ?Sized> Deref for InRef<'i, T> {
|
||||
/// This is similar to [`In`] but takes a mutable reference to a value instead of the value itself.
|
||||
/// See [`InRef`] for the read-only version.
|
||||
///
|
||||
/// See [`SystemInput`] to learn more about system inputs in general.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Here is a simple example of a system that takes a `&mut usize` and squares it.
|
||||
@ -217,6 +239,8 @@ impl<E: 'static, B: Bundle> SystemInput for Trigger<'_, E, B> {
|
||||
///
|
||||
/// This makes it useful for having arbitrary [`SystemInput`]s in
|
||||
/// function systems.
|
||||
///
|
||||
/// See [`SystemInput`] to learn more about system inputs in general.
|
||||
pub struct StaticSystemInput<'a, I: SystemInput>(pub I::Inner<'a>);
|
||||
|
||||
impl<'a, I: SystemInput> SystemInput for StaticSystemInput<'a, I> {
|
||||
@ -227,3 +251,66 @@ impl<'a, I: SystemInput> SystemInput for StaticSystemInput<'a, I> {
|
||||
StaticSystemInput(this)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_system_input_tuple {
|
||||
($(#[$meta:meta])* $($name:ident),*) => {
|
||||
$(#[$meta])*
|
||||
impl<$($name: SystemInput),*> SystemInput for ($($name,)*) {
|
||||
type Param<'i> = ($($name::Param<'i>,)*);
|
||||
type Inner<'i> = ($($name::Inner<'i>,)*);
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(clippy::unused_unit)]
|
||||
fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> {
|
||||
let ($($name,)*) = this;
|
||||
($($name::wrap($name),)*)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
all_tuples!(
|
||||
#[doc(fake_variadic)]
|
||||
impl_system_input_tuple,
|
||||
0,
|
||||
8,
|
||||
I
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
system::{In, InMut, InRef, IntoSystem, System},
|
||||
world::World,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn two_tuple() {
|
||||
fn by_value((In(a), In(b)): (In<usize>, In<usize>)) -> usize {
|
||||
a + b
|
||||
}
|
||||
fn by_ref((InRef(a), InRef(b)): (InRef<usize>, InRef<usize>)) -> usize {
|
||||
*a + *b
|
||||
}
|
||||
fn by_mut((InMut(a), In(b)): (InMut<usize>, In<usize>)) {
|
||||
*a += b;
|
||||
}
|
||||
|
||||
let mut world = World::new();
|
||||
let mut by_value = IntoSystem::into_system(by_value);
|
||||
let mut by_ref = IntoSystem::into_system(by_ref);
|
||||
let mut by_mut = IntoSystem::into_system(by_mut);
|
||||
|
||||
by_value.initialize(&mut world);
|
||||
by_ref.initialize(&mut world);
|
||||
by_mut.initialize(&mut world);
|
||||
|
||||
let mut a = 12;
|
||||
let b = 24;
|
||||
|
||||
assert_eq!(by_value.run((a, b), &mut world), 36);
|
||||
assert_eq!(by_ref.run((&a, &b), &mut world), 36);
|
||||
by_mut.run((&mut a, b), &mut world);
|
||||
assert_eq!(a, 36);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user