能否编写Rust宏build_fn生成元组元素?附Bevy实践尝试
实现
build_fn宏的需求与解决方案尝试 需求说明
需要编写build_fn宏,实现将指定类型列表与函数列表组合,生成每个函数针对每个类型的具体化版本并嵌入元组。示例如下:
fn foo<T>() { println!("foo: {:?}", std::any::type_name::<T>()); } let functions = (foo::<i32>, build_fn!(i64,u64,f32,f64;foo), foo::<u32>); // 最终期望展开为: let functions = (foo::<i32>, foo::<i64>, foo::<u64>, foo::<f32>, foo::<f64>, foo::<u32>); // 更复杂场景支持多函数: // build_fn!(i64,u64,f32,f64;foo,boo) // 期望展开为 (foo::<i64>, boo::<i64>, foo::<u64>, boo::<u64>, ...)
临时macro_rules方案(基于Bevy ECS实现)
在Bevy开发中,已实现一个可用但略显繁琐的macro_rules方案:
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)尝试及问题
后续尝试使用过程宏实现,但目前无法正确生成元组子元素,代码如下:
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)*) }) }
内容的提问来源于stack exchange,提问作者Fantasy_RQG
相关产品推荐
相关产品推荐

