2021-01-08 03:50:09 +00:00
|
|
|
use std::any::Any;
|
|
|
|
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
use crate::{serde::Serializable, FromReflect, Reflect, ReflectMut, ReflectRef};
|
2021-01-08 03:50:09 +00:00
|
|
|
|
2022-01-08 20:45:24 +00:00
|
|
|
/// A reflected Rust tuple.
|
|
|
|
///
|
|
|
|
/// This trait is automatically implemented for arbitrary tuples of up to 12
|
|
|
|
/// elements, provided that each element implements [`Reflect`].
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use bevy_reflect::Tuple;
|
|
|
|
///
|
|
|
|
/// # fn main() {
|
|
|
|
/// let foo = ("blue".to_string(), 42_i32);
|
|
|
|
/// assert_eq!(foo.field_len(), 2);
|
|
|
|
///
|
|
|
|
/// let first = foo.field(0).unwrap();
|
|
|
|
/// assert_eq!(first.downcast_ref::<String>(), Some(&"blue".to_string()));
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2021-01-08 03:50:09 +00:00
|
|
|
pub trait Tuple: Reflect {
|
2022-01-08 20:45:24 +00:00
|
|
|
/// Returns a reference to the value of the field with index `index` as a
|
|
|
|
/// `&dyn Reflect`.
|
2021-01-08 03:50:09 +00:00
|
|
|
fn field(&self, index: usize) -> Option<&dyn Reflect>;
|
2022-01-08 20:45:24 +00:00
|
|
|
|
|
|
|
/// Returns a mutable reference to the value of the field with index `index`
|
|
|
|
/// as a `&mut dyn Reflect`.
|
2021-01-08 03:50:09 +00:00
|
|
|
fn field_mut(&mut self, index: usize) -> Option<&mut dyn Reflect>;
|
2022-01-08 20:45:24 +00:00
|
|
|
|
|
|
|
/// Returns the number of fields in the tuple.
|
2021-01-08 03:50:09 +00:00
|
|
|
fn field_len(&self) -> usize;
|
2022-01-08 20:45:24 +00:00
|
|
|
|
|
|
|
/// Returns an iterator over the values of the tuple's fields.
|
2021-01-08 03:50:09 +00:00
|
|
|
fn iter_fields(&self) -> TupleFieldIter;
|
2022-01-08 20:45:24 +00:00
|
|
|
|
|
|
|
/// Clones the struct into a [`DynamicTuple`].
|
2021-01-08 03:50:09 +00:00
|
|
|
fn clone_dynamic(&self) -> DynamicTuple;
|
|
|
|
}
|
|
|
|
|
2022-01-08 20:45:24 +00:00
|
|
|
/// An iterator over the field values of a tuple.
|
2021-01-08 03:50:09 +00:00
|
|
|
pub struct TupleFieldIter<'a> {
|
|
|
|
pub(crate) tuple: &'a dyn Tuple,
|
|
|
|
pub(crate) index: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> TupleFieldIter<'a> {
|
|
|
|
pub fn new(value: &'a dyn Tuple) -> Self {
|
|
|
|
TupleFieldIter {
|
|
|
|
tuple: value,
|
|
|
|
index: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for TupleFieldIter<'a> {
|
|
|
|
type Item = &'a dyn Reflect;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
let value = self.tuple.field(self.index);
|
|
|
|
self.index += 1;
|
|
|
|
value
|
|
|
|
}
|
2021-04-13 01:28:14 +00:00
|
|
|
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
let size = self.tuple.field_len();
|
|
|
|
(size, Some(size))
|
|
|
|
}
|
2021-01-08 03:50:09 +00:00
|
|
|
}
|
|
|
|
|
2021-04-13 01:28:14 +00:00
|
|
|
impl<'a> ExactSizeIterator for TupleFieldIter<'a> {}
|
|
|
|
|
2022-01-08 20:45:24 +00:00
|
|
|
/// A convenience trait which combines fetching and downcasting of tuple
|
|
|
|
/// fields.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use bevy_reflect::GetTupleField;
|
|
|
|
///
|
|
|
|
/// # fn main() {
|
|
|
|
/// let foo = ("blue".to_string(), 42_i32);
|
|
|
|
///
|
|
|
|
/// assert_eq!(foo.get_field::<String>(0), Some(&"blue".to_string()));
|
|
|
|
/// assert_eq!(foo.get_field::<i32>(1), Some(&42));
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2021-01-08 03:50:09 +00:00
|
|
|
pub trait GetTupleField {
|
2022-01-08 20:45:24 +00:00
|
|
|
/// Returns a reference to the value of the field with index `index`,
|
|
|
|
/// downcast to `T`.
|
2021-01-08 03:50:09 +00:00
|
|
|
fn get_field<T: Reflect>(&self, index: usize) -> Option<&T>;
|
2022-01-08 20:45:24 +00:00
|
|
|
|
|
|
|
/// Returns a mutable reference to the value of the field with index
|
|
|
|
/// `index`, downcast to `T`.
|
2021-01-08 03:50:09 +00:00
|
|
|
fn get_field_mut<T: Reflect>(&mut self, index: usize) -> Option<&mut T>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<S: Tuple> GetTupleField for S {
|
|
|
|
fn get_field<T: Reflect>(&self, index: usize) -> Option<&T> {
|
|
|
|
self.field(index)
|
|
|
|
.and_then(|value| value.downcast_ref::<T>())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_field_mut<T: Reflect>(&mut self, index: usize) -> Option<&mut T> {
|
|
|
|
self.field_mut(index)
|
|
|
|
.and_then(|value| value.downcast_mut::<T>())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GetTupleField for dyn Tuple {
|
|
|
|
fn get_field<T: Reflect>(&self, index: usize) -> Option<&T> {
|
|
|
|
self.field(index)
|
|
|
|
.and_then(|value| value.downcast_ref::<T>())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_field_mut<T: Reflect>(&mut self, index: usize) -> Option<&mut T> {
|
|
|
|
self.field_mut(index)
|
|
|
|
.and_then(|value| value.downcast_mut::<T>())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-08 20:45:24 +00:00
|
|
|
/// A tuple which allows fields to be added at runtime.
|
2021-01-08 03:50:09 +00:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct DynamicTuple {
|
2021-02-02 21:57:26 +00:00
|
|
|
name: String,
|
|
|
|
fields: Vec<Box<dyn Reflect>>,
|
2021-01-08 03:50:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DynamicTuple {
|
2022-01-14 19:09:44 +00:00
|
|
|
/// Returns the type name of the tuple.
|
2022-01-08 20:45:24 +00:00
|
|
|
///
|
|
|
|
/// The tuple's name is automatically generated from its element types.
|
2021-02-02 21:57:26 +00:00
|
|
|
pub fn name(&self) -> &str {
|
|
|
|
&self.name
|
|
|
|
}
|
|
|
|
|
2022-01-14 19:09:44 +00:00
|
|
|
/// Manually sets the type name of the tuple.
|
2022-01-08 20:45:24 +00:00
|
|
|
///
|
|
|
|
/// Note that the tuple name will be overwritten when elements are added.
|
2021-02-02 21:57:26 +00:00
|
|
|
pub fn set_name(&mut self, name: String) {
|
|
|
|
self.name = name;
|
|
|
|
}
|
|
|
|
|
2022-01-08 20:45:24 +00:00
|
|
|
/// Appends an element with value `value` to the tuple.
|
2021-01-08 03:50:09 +00:00
|
|
|
pub fn insert_boxed(&mut self, value: Box<dyn Reflect>) {
|
|
|
|
self.fields.push(value);
|
2021-02-02 21:57:26 +00:00
|
|
|
self.generate_name();
|
2021-01-08 03:50:09 +00:00
|
|
|
}
|
|
|
|
|
2022-01-08 20:45:24 +00:00
|
|
|
/// Appends a typed element with value `value` to the tuple.
|
2021-01-08 03:50:09 +00:00
|
|
|
pub fn insert<T: Reflect>(&mut self, value: T) {
|
|
|
|
self.insert_boxed(Box::new(value));
|
2021-02-02 21:57:26 +00:00
|
|
|
self.generate_name();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn generate_name(&mut self) {
|
|
|
|
let name = &mut self.name;
|
|
|
|
name.clear();
|
|
|
|
name.push('(');
|
|
|
|
for (i, field) in self.fields.iter().enumerate() {
|
|
|
|
if i > 0 {
|
|
|
|
name.push_str(", ");
|
|
|
|
}
|
|
|
|
name.push_str(field.type_name());
|
|
|
|
}
|
|
|
|
name.push(')');
|
2021-01-08 03:50:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Tuple for DynamicTuple {
|
|
|
|
#[inline]
|
|
|
|
fn field(&self, index: usize) -> Option<&dyn Reflect> {
|
|
|
|
self.fields.get(index).map(|field| &**field)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn field_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {
|
|
|
|
self.fields.get_mut(index).map(|field| &mut **field)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn field_len(&self) -> usize {
|
|
|
|
self.fields.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn iter_fields(&self) -> TupleFieldIter {
|
|
|
|
TupleFieldIter {
|
|
|
|
tuple: self,
|
|
|
|
index: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn clone_dynamic(&self) -> DynamicTuple {
|
|
|
|
DynamicTuple {
|
2021-02-02 21:57:26 +00:00
|
|
|
name: self.name.clone(),
|
2021-01-08 03:50:09 +00:00
|
|
|
fields: self
|
|
|
|
.fields
|
|
|
|
.iter()
|
|
|
|
.map(|value| value.clone_value())
|
|
|
|
.collect(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-17 22:46:46 +00:00
|
|
|
// SAFE: any and any_mut both return self
|
|
|
|
unsafe impl Reflect for DynamicTuple {
|
2021-01-08 03:50:09 +00:00
|
|
|
#[inline]
|
|
|
|
fn type_name(&self) -> &str {
|
2021-02-02 21:57:26 +00:00
|
|
|
self.name()
|
2021-01-08 03:50:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn any(&self) -> &dyn Any {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn any_mut(&mut self) -> &mut dyn Any {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn clone_value(&self) -> Box<dyn Reflect> {
|
|
|
|
Box::new(self.clone_dynamic())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn reflect_ref(&self) -> ReflectRef {
|
|
|
|
ReflectRef::Tuple(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn reflect_mut(&mut self) -> ReflectMut {
|
|
|
|
ReflectMut::Tuple(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn apply(&mut self, value: &dyn Reflect) {
|
|
|
|
tuple_apply(self, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
|
|
|
*self = value.take()?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reflect_hash(&self) -> Option<u64> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
|
|
|
|
tuple_partial_eq(self, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serializable(&self) -> Option<Serializable> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-14 19:09:44 +00:00
|
|
|
/// Applies the elements of `b` to the corresponding elements of `a`.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// This function panics if `b` is not a tuple.
|
2021-01-08 03:50:09 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn tuple_apply<T: Tuple>(a: &mut T, b: &dyn Reflect) {
|
|
|
|
if let ReflectRef::Tuple(tuple) = b.reflect_ref() {
|
|
|
|
for (i, value) in tuple.iter_fields().enumerate() {
|
|
|
|
if let Some(v) = a.field_mut(i) {
|
|
|
|
v.apply(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
panic!("Attempted to apply non-Tuple type to Tuple type.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-14 19:09:44 +00:00
|
|
|
/// Compares a [`Tuple`] with a [`Reflect`] value.
|
|
|
|
///
|
|
|
|
/// Returns true if and only if all of the following are true:
|
|
|
|
/// - `b` is a tuple;
|
|
|
|
/// - `b` has the same number of elements as `a`;
|
|
|
|
/// - [`Reflect::reflect_partial_eq`] returns `Some(true)` for pairwise elements of `a` and `b`.
|
2021-01-08 03:50:09 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn tuple_partial_eq<T: Tuple>(a: &T, b: &dyn Reflect) -> Option<bool> {
|
|
|
|
let b = if let ReflectRef::Tuple(tuple) = b.reflect_ref() {
|
|
|
|
tuple
|
|
|
|
} else {
|
|
|
|
return Some(false);
|
|
|
|
};
|
|
|
|
|
|
|
|
if a.field_len() != b.field_len() {
|
|
|
|
return Some(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (a_field, b_field) in a.iter_fields().zip(b.iter_fields()) {
|
|
|
|
match a_field.reflect_partial_eq(b_field) {
|
|
|
|
Some(false) | None => return Some(false),
|
|
|
|
Some(true) => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! impl_reflect_tuple {
|
|
|
|
{$($index:tt : $name:tt),*} => {
|
|
|
|
impl<$($name: Reflect),*> Tuple for ($($name,)*) {
|
|
|
|
#[inline]
|
|
|
|
fn field(&self, index: usize) -> Option<&dyn Reflect> {
|
|
|
|
match index {
|
|
|
|
$($index => Some(&self.$index as &dyn Reflect),)*
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn field_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {
|
|
|
|
match index {
|
|
|
|
$($index => Some(&mut self.$index as &mut dyn Reflect),)*
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn field_len(&self) -> usize {
|
|
|
|
let indices: &[usize] = &[$($index as usize),*];
|
|
|
|
indices.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn iter_fields(&self) -> TupleFieldIter {
|
|
|
|
TupleFieldIter {
|
|
|
|
tuple: self,
|
|
|
|
index: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn clone_dynamic(&self) -> DynamicTuple {
|
2021-02-02 21:57:26 +00:00
|
|
|
let mut dyn_tuple = DynamicTuple {
|
|
|
|
name: String::default(),
|
2021-01-08 03:50:09 +00:00
|
|
|
fields: self
|
|
|
|
.iter_fields()
|
|
|
|
.map(|value| value.clone_value())
|
|
|
|
.collect(),
|
2021-02-02 21:57:26 +00:00
|
|
|
};
|
|
|
|
dyn_tuple.generate_name();
|
|
|
|
dyn_tuple
|
2021-01-08 03:50:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-17 22:46:46 +00:00
|
|
|
// SAFE: any and any_mut both return self
|
|
|
|
unsafe impl<$($name: Reflect),*> Reflect for ($($name,)*) {
|
2021-01-08 03:50:09 +00:00
|
|
|
fn type_name(&self) -> &str {
|
|
|
|
std::any::type_name::<Self>()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn any(&self) -> &dyn Any {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn any_mut(&mut self) -> &mut dyn Any {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn apply(&mut self, value: &dyn Reflect) {
|
|
|
|
crate::tuple_apply(self, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
|
|
|
*self = value.take()?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reflect_ref(&self) -> ReflectRef {
|
|
|
|
ReflectRef::Tuple(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reflect_mut(&mut self) -> ReflectMut {
|
|
|
|
ReflectMut::Tuple(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn clone_value(&self) -> Box<dyn Reflect> {
|
|
|
|
Box::new(self.clone_dynamic())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reflect_hash(&self) -> Option<u64> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
|
|
|
|
crate::tuple_partial_eq(self, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serializable(&self) -> Option<Serializable> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
|
|
|
|
impl<$($name: FromReflect),*> FromReflect for ($($name,)*)
|
|
|
|
{
|
|
|
|
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
|
|
|
|
if let ReflectRef::Tuple(_ref_tuple) = reflect.reflect_ref() {
|
|
|
|
Some(
|
|
|
|
(
|
|
|
|
$(
|
|
|
|
<$name as FromReflect>::from_reflect(_ref_tuple.field($index)?)?,
|
|
|
|
)*
|
|
|
|
)
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-08 03:50:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_reflect_tuple! {}
|
|
|
|
impl_reflect_tuple! {0: A}
|
|
|
|
impl_reflect_tuple! {0: A, 1: B}
|
|
|
|
impl_reflect_tuple! {0: A, 1: B, 2: C}
|
|
|
|
impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D}
|
|
|
|
impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E}
|
|
|
|
impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E, 5: F}
|
|
|
|
impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G}
|
|
|
|
impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H}
|
|
|
|
impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I}
|
|
|
|
impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J}
|
|
|
|
impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K}
|
|
|
|
impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K, 11: L}
|