现在有一个需求,有如下结构体
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
}
}
我自己实现了一个,大家可以看看,支持多个可选择选型参函数,使用如下
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
}
}
}
过程宏代码如下
#[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()
}
use std::collections::{HashMap};
use quote::TokenStreamExt;
use syn::Token;
#[derive(Debug)]
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> {
let _attrs:Vec<syn::Attribute>=input.call(syn::Attribute::parse_outer)?;
// 解析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!(#(#_attrs)* #_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.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),*>));
}
// 将泛型参数列表后的属于本impl块所有TokenStream保存到待查找的TokenStream中,这里采用查找到最外层{}包裹块为结束
let last=input.step(|c|{
let mut res=*c;
let mut temp_tokenstream=proc_macro2::TokenStream::new();
while let Some((tt,next)) = res.token_tree() {
temp_tokenstream.append(tt.clone());
match &tt{
proc_macro2::TokenTree::Group(g) if g.delimiter()==proc_macro2::Delimiter::Brace=>{
return Ok((temp_tokenstream,next));
},
_=>res=next,
}
}
Err(c.error("错误的impl"))
})?;
segments.extend(last);
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()&&(i==0||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).expect(&format!("ident错误:{}",ident.to_string()));
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>
}
}
//可支持函数式过程宏中书写多个impl块
pub struct GenericsSelects{
gs:Vec<GenericsSelect>
}
impl syn::parse::Parse for GenericsSelects{
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut gs=vec![];
while !input.is_empty() {
gs.push(input.parse()?);
}
Ok(Self{
gs
})
}
}
impl quote::ToTokens for GenericsSelects{
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
for g in &self.gs{
tokens.extend(quote::quote!(#g))
}
}
}
1
共 6 条评论, 1 页
评论区
写评论感谢解答
👇
苦瓜小仔:
可以使用,在这里的常量作用域内声明 impl,和模块内声明是一样的。
我的总结是:
你这个不是后面转换为
匿名作用域内的函数在外面还能使用吗?
impl 是“全局静态”的,一旦写明就存在,无论在哪里写明。这里的哪里指任何可以定义 Item 的地方,包括且不局限于常量作用域、不同的模块(子模块、父模块甚至很远的路径下的模块,只要在孤儿原则之内)。
使用范围由 pub(...) 决定,与 impl 无关。
但是这样就没法在外部使用impl块中定义的函数了吧
--
👇
苦瓜小仔: 我也写了一个:https://github.com/zjp-CN/genericity_select/blob/main/_impl/lib.rs
语法是属性宏:
我也写了一个:https://github.com/zjp-CN/genericity_select/blob/main/_impl/lib.rs
语法是属性宏: