Search code examples
rustrust-macros

Is it possible to use rust macro to build items of a tuple?


fn foo<T> { println!("foo: {:?}", std::any::type_name::<T>()); }
let functions = (foo::<i32>,build_fn!(i64,u64,f32,f64;foo),foo::<u32>);

// the final `functions` is 
let functions = (foo::<i32>, foo::<i64>, foo::<u64>, foo::<f32>, foo::<f64> ,foo::<u32>);

// maybe more complex like build_fn!(i64,u64,f32,f64;foo,boo)

Is it possible to write build_fn macro ?

------------- edit line --------------

In my case, I was doing something with bevy. I managed to get something like this. Which is little ackward solution, but still working thanks to the flexibility of bevy ecs.

macro_rules! unit_team_system {
    ($($team:ty),* ; $sys:tt) => {
        ($(unit_team_system!(@make_fun $team; $sys),)*)
    };
    (@make_fun $team:ty; ($($sys:ident),* $(,)?) ) =>{
        ($($sys::<$team>,)*)
    };
}

#[derive(Debug)]
struct TeamA;

#[derive(Debug)]
struct TeamB;

#[derive(Debug)]
struct TeamC;

fn system_a<T>() {
    println!("system_a: {:?}", std::any::type_name::<T>());
}

fn system_b<T>() {
    println!("system_b: {:?}", std::any::type_name::<T>());
}

fn main() {
    let systems = unit_team_system!(TeamA, TeamB, TeamC;
               (
                system_a,
                system_b,
                )
        );

    systems.1.0();
}

----------------- proc_macro -----------------
Still not posssible to generate subitems of a tuple.

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{Ident, Token, Type};
use syn::parse::{Parse, ParseStream};


struct UnitTeamSystem {
    teams: Vec<Type>,
    systems: Vec<Ident>,
}

impl Parse for UnitTeamSystem {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let mut teams = Vec::new();
        let mut systems = Vec::new();
        while !input.is_empty() {
            if input.peek(Token![;]) {
                break;
            }
            if input.peek(Token![,]) {
                input.parse::<Token![,]>()?;
            }
            let team: Type = input.parse()?;
            teams.push(team);
        }

        input.parse::<Token![;]>()?;

        while !input.is_empty() {
            let system: Ident = input.parse()?;
            systems.push(system);
            if input.peek(Token![,]) {
                input.parse::<Token![,]>()?;
            }
        }
        Ok(UnitTeamSystem {
            teams,
            systems,
        })
    }
}

#[proc_macro]
pub fn unit_team_system(input: TokenStream) -> TokenStream {
    let UnitTeamSystem { teams, systems } = syn::parse_macro_input!(input as UnitTeamSystem);

    let mut tokens = Vec::with_capacity(teams.len());

    for team in teams {
        for system in &systems {
            tokens.push(quote! {
                #system::<#team>,
            });
        }
    }

    eprintln!("{:?}", tokens);

    TokenStream::from(quote! {
        (#(#tokens)*)
    })
}

Solution

  • Is it possible to write build_fn macro ?

    Not with declarative macros, because a declarative macro has to generate an entire language item (statement, expression, type, pattern, ...).

    Here you're trying to generate a section of tuple, which is not an item of the rust language.

    It might be possible using a procedural macro, because IIRC procedural macros operate in terms of token streams, so while they are generally used to generate items, they might be able to generate sub-items or partial items (I've never actually used them that way).