< 返回版块

Wuyan 发表于 2022-06-24 14:56

Tags:泛型,宏

现在有一个需求,有如下结构体

pub struct Vec2<T>{
    x:T,
    y:T,
}

在实现结构体方法时使用genericity_select宏修饰impl块,使i该impl块内T可以使用所有u8、u16共同的方法

#[genericity_select]
impl<T=u8|u16> Vec2<T> {
    // 在这个作用域内 T 能够使用所有u8、u6共同的方法
    pub const fn new(x:T,y:T)->Self{
        
        Self { x, y}
    }
    pub const fn distance(&self)->T{
        self.x*self.x + self.y*self.y
    }
}

以上写法等同于转化为以下实现

impl Vec2<u8> {
    // 在这个作用域内使用所有u8、u16共同的方法
    pub const fn new(x:u8,y:u8)->Self{
        Self { x, y}
    }
    pub const fn distance(&self)->u8{
        self.x*self.x + self.y*self.y
    }
}
impl Vec2<u16> {
    // 在这个作用域内使用所有u8、u16共同的方法
    pub const fn new(x:u16,y:u16)->Self{
        Self { x, y}
    }
    pub const fn distance(&self)->u16{
        self.x*self.x + self.y*self.y
    }
}

求教现在是否有这样的宏?

评论区

写评论
作者 Wuyan 2022-06-25 20:35

我自己实现了一个,大家可以看看,支持多个可选择选型参函数,使用如下

struct Vec2<T,U>{
    x:T,
    y:U,
}
blui_derive::genericity_select!{
    impl <T=i32|u8|u64,U:u8|u16> Vec2<T,U>{
        fn dis(&self)->T{
            self.x*self.x
        }
    }
}

过程宏代码如下

use std::collections::{HashMap};
use syn::Token;

#[proc_macro]
pub fn genericity_select(input:proc_macro::TokenStream)->proc_macro::TokenStream{
    let bofy_ast=parse_macro_input!(input as generic_select::GenericsSelect);
    quote::quote!(#bofy_ast).into()
}
#[derive(Debug)]
pub struct GenericsSelect{
    /// 可选泛型参数前面部分,一般为 `impl`
    prefix:proc_macro2::TokenStream,
    selects:HashMap<syn::Ident,Vec<syn::Type>>,
    /// 除去前缀prefix和可选泛型参数剩余部分
    segments:Vec<proc_macro2::TokenTree>,
    /// segments中具有的可选泛型参数Ident的位置偏移信息
    index:Vec<Index>,
}
impl syn::parse::Parse for GenericsSelect{
    /// 解析输入的TokenStream
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        // 解析impl块前缀信息泛型,参数列表以前的部分
        let _defaultness: Option<Token![default]> = input.parse()?;
        let _unsafety: Option<Token![unsafe]> = input.parse()?;
        let _impl_token: Token![impl] = input.parse()?;
        let prefix=quote::quote!(#_defaultness #_unsafety #_impl_token);

        // 解析impl块中泛型可选参数部分,形如 <A=u8|u16,B=i8|i16,C:Clone> 中的 "A=u8|u16"、"B=i8|i16",并缓存其他泛型参数,如 "C:Clone"
        let mut segments=proc_macro2::TokenStream::new();
        let mut selects=HashMap::new();
        let mut count=0;
        if input.parse::<Token!(<)>().is_ok() {
            count+=1;
        }
        // 缓存其他泛型参数,如 "C:Clone"
        let mut g=Vec::<syn::GenericParam>::new();
        while count>0 {
            if input.peek(Token!(>)){
                input.parse::<Token!(>)>()?;
                break;
            }else {
                if input.peek(syn::Ident){
                    // 解析可选泛型参数
                    if input.peek2(Token!(=)) {
                        let select_ident=input.parse::<syn::Ident>()?;
                        input.parse::<Token!(=)>()?;
                        let mut select_tys=vec![input.parse::<syn::Type>()?];
                        while !(input.peek(Token!(,))||input.peek(Token!(>))) {
                            input.parse::<Token!(|)>()?;
                            select_tys.push(input.parse::<syn::Type>()?);
                        }
                        selects.insert(select_ident,select_tys);
                    }
                    // 解析rust标准语法泛型参数
                    else {
                        g.push(input.parse::<syn::GenericParam>()?);
                    }
                    if input.peek(Token![,]) {
                        input.parse::<Token![,]>()?;
                    }
                }
            }
        }

        // 将缓存的其他泛型参数(如 "C:Clone")保存到待查找的TokenStream中
        if g.len()>0 {
            segments.extend(quote::quote!(<#(#g),*>));
        }
        // 将泛型参数列表后的所有TokenStream保存到待查找的TokenStream中
        segments.extend(input.parse::<proc_macro2::TokenStream>()?);
        let segments=segments.into_iter().collect();
        // 在TokenStream中查找所有可选泛型标识符的位置(如TokenStream中所有ident A、B的位置)
        let index=find_select_index(&segments, &selects);
        Ok(Self{
            prefix,
            selects,
            segments,
            index,
        })
    }
}
/// 在TokenStream中查找所有可选泛型标识符的位置
fn find_select_index(input:&Vec<proc_macro2::TokenTree>,selects:&HashMap<syn::Ident,Vec<syn::Type>>)->Vec<Index>{
    let mut index=Vec::new();
    for i in 0..input.len(){
        match &input[i] {
            // 如果是()、[]、{}包围的就需要递归查找
            proc_macro2::TokenTree::Group(temp) => {
                let child=find_select_index(&temp.stream().into_iter().collect(),selects);
                index.push(Index::Group { offset: i, child: child })
            },
            // 如果当前ident是代表泛型参数的ident就记录位置,保证高ident不是一个除了路径的开头部分
            proc_macro2::TokenTree::Ident(temp) => {
                if selects.get(temp).is_some()&&match &input[i-1]{
                    proc_macro2::TokenTree::Group(_) => true,
                    proc_macro2::TokenTree::Ident(_) => true,
                    proc_macro2::TokenTree::Punct(p) => {
                        if p.as_char()=='.'{
                            false
                        }else if p.as_char()==':'&&i-2>0 {
                            match &input[i-2] {
                                proc_macro2::TokenTree::Punct(p) if p.as_char()==':'&&p.spacing()==proc_macro2::Spacing::Joint=> {
                                    false
                                },
                                _=> true,
                            }
                        }
                        else {
                            true
                        }
                    },
                    proc_macro2::TokenTree::Literal(_) => true,
                }{
                    index.push(Index::Current { offset: i, ident: temp.clone() })
                }
            },
            _ => {
                
            },
        }
    }
    index
}
impl quote::ToTokens for GenericsSelect {
    /// 转化为TokenStream
    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
        // 如果待替换TokenStream没有可选泛型参数信息,直接返回原TokenStream
        if self.index.len()==0 {
            tokens.extend(self.prefix.clone());
            tokens.extend(self.segments.clone());
        }else{
            // 总共需要替换的变种数目
            let mut count=1;
            let selects=self.selects.clone().into_iter().collect::<Vec<_>>();
            // 辅助选择各个段选择的数组
            let mut pp=(0..selects.len()).map(|_|1usize).collect::<Vec<_>>();
            // 计算替换的变种数目、辅助数组
            for i in 0..selects.len()  {
                pp[selects.len()-1-i]=count;
                count*=selects[selects.len()-1-i].1.len();                
            }
            // eprintln!("pp:{:?}",&pp);
            // eprintln!("selects:{:?}",&selects);
            // 每个替换变种的各个可选泛型参数的替换类型
            let mut infos=HashMap::new();
            for i in 0..count{
                let mut old=i;
                // let mut ttt=vec![];
                // 计算本次各个可选泛型参数的替换类型
                for select_index in 0..selects.len()  {
                    //ttt.push(old/pp[select_index]);
                    infos.insert(selects[select_index].0.clone(), selects[select_index].1[old/pp[select_index]].clone());
                    old=old%pp[select_index];
                }
                //eprintln!("i={i}->{:?}",ttt);
                // 执行替换
                let temp=replace_ident(&infos,&self.segments,&self.index);
                let pre=&self.prefix;
                
                tokens.extend(quote::quote!(#pre #temp));
            }
        }
    }
}
fn replace_ident(infos:&HashMap<syn::Ident,syn::Type>,input:&Vec<proc_macro2::TokenTree>,indexs:&Vec<Index>)->proc_macro2::TokenStream{
    let mut res=proc_macro2::TokenStream::new();
    let mut last_offset=0;
    for i in 0..indexs.len() {
        match &indexs[i] {
            Index::Current { offset, ident } => {
                let pre=&input[last_offset..*offset];
                res.extend(quote::quote!(#(#pre)*));
                last_offset=*offset+1;
                let ty=infos.get(ident).unwrap();
                res.extend(quote::quote!(#ty));
            },
            Index::Group { offset, child } => {
                let pre=&input[last_offset..*offset];
                res.extend(quote::quote!(#(#pre)*));
                last_offset=*offset+1;
                match &input[*offset] {
                    proc_macro2::TokenTree::Group(g) => {
                        let new_stream=replace_ident(infos, &g.stream().into_iter().collect(), child);
                        let new_g=proc_macro2::Group::new(g.delimiter(), new_stream);
                        res.extend(quote::quote!(#new_g));
                    },
                    _=>{
                        
                    }
                }
            },
        }
    }
    let last=&input[last_offset..];
    res.extend(quote::quote!(#(#last)*));
    res
}
/// 可选泛型参数的索引
#[derive(Debug)]
enum Index {
    Current{
        /// 可选泛型参数在当前组中的偏移量
        offset:usize,
        /// 可选泛型参数的Ident
        ident:syn::Ident,
    },
    /// 用于递归查找()、[]、{}中的可选泛型
    Group{
        /// ()、[]、{}的偏移量
        offset:usize,
        /// ()、[]、{}的中的可选泛型参数位置信息
        child:Vec<Index>
    }
}
作者 Wuyan 2022-06-24 19:42

非常感谢解答,但还是想要这样一种宏实现更通用的方法

uno 2022-06-24 15:24

简单写个声明宏就行了


macro_rules! genericity_select {
     ( $($t:ty),* ) => {
         $(
            impl Vec2<$t> {
                pub const fn new(x:$t,y:$t)->Self{
                    Self { x, y}
                }
                pub const fn distance(&self)->$t{
                    self.x*self.x + self.y*self.y
                }
            }
        )*
    };
}

genericity_select!(u8,u16);

1 共 3 条评论, 1 页