Home > Back-end >  Generics with any number of generic?
Generics with any number of generic?

Time:08-16

Making an ecs, I've been trying to create entities from a few components, using any number of these for user simplicity :

    pub fn create_entity_with_1<C: Component<C>   'static>(&mut self, component: C) -> Entity {
        // C is a component
        let result: Entity = Entity {
            id: self.last_entity_id,
        };
        self.last_entity_id  = 1;
        // add the component, as we just created the entity we can fast push in the packed array
        self.components.add_comp_to_last(&result, component);
        return result;
    }

    pub fn create_entity_with_2<C1: Component<C1>   'static,
                                C2: Component<C2>   'static>
                                (&mut self, component_1: C1,
                                            component_2: C2) -> Entity {
        // C is a component
        let result: Entity = Entity {
            id: self.last_entity_id,
        };
        self.last_entity_id  = 1;
        // add the component, as we just created the entity we can fast push in the packed array
        self.components.add_comp_to_last(&result, component_1);
        self.components.add_comp_to_last(&result, component_2);

        return result;
    }

Clearly, if I extend this to more components, the pattern of this function will always be the same, and I don't want to have 15 of these.

Is there a way to write a generic taking arbitrary number of generic components ?

Alternatively, I've seen The legion ecs does this taking a tuple of components, but I can't figure out how to unpack tuples of any sizes.

CodePudding user response:

You can do that with a macro:

macro_rules! create_entity_fns {
    (
        $first_fn_name:ident $first_generic:ident
        $( $fn_name:ident $generic:ident )*
    ) => {
        #[allow(non_snake_case)]
        pub fn $first_fn_name<
            $first_generic: Component<$first_generic>   'static,
            $( $generic: Component<$generic>   'static, )*
        >(
            &mut self,
            $first_generic: $first_generic,
            $( $generic: $generic, )*
        ) -> Entity {
            let result = Entity { id: self.last_entity_id };
            self.last_entity_id  = 1;

            self.components.add_to_last(&result, $first_generic);
            $( self.components.add_to_last(&result, $generic); )*

            result
        }
        
        create_entity_fns!( $( $fn_name $generic )* );
    };

    () => {};
}

impl Foo {
    create_entity_fns!(
        // Notice the swapped order!
        // ...
        create_entity_with_4 C4
        create_entity_with_3 C3
        create_entity_with_2 C2
        create_entity_with_1 C1
    );
}

For each invocation we create a function and recurse to the next invocation with one less parameters.

  • Related