Macros 在宏展开中使用参数编号?
将参数扩展到宏时,是否有方法将参数编号包含在宏中 下面是一个完整的示例,展示了如何使用trait将索引分配给结构。当前Macros 在宏展开中使用参数编号?,macros,rust,Macros,Rust,将参数扩展到宏时,是否有方法将参数编号包含在宏中 下面是一个完整的示例,展示了如何使用trait将索引分配给结构。当前struct\u number()始终返回0,是否可以根据宏参数的顺序返回常量 struct Foo {_var: bool} struct Bar {_var: u8} struct Baz {_var: i16} trait NumberStruct { fn struct_number() -> usize; } macro_rules! number_s
struct\u number()
始终返回0,是否可以根据宏参数的顺序返回常量
struct Foo {_var: bool}
struct Bar {_var: u8}
struct Baz {_var: i16}
trait NumberStruct {
fn struct_number() -> usize;
}
macro_rules! number_structs_impl {
($($t:ty)*) => ($(
impl NumberStruct for $t {
fn struct_number() -> usize {
// How to return a number based on the argument order?
return 0;
}
}
)*)
}
number_structs_impl!(Foo Bar Baz);
fn main() {
// see if the numbers are correct
macro_rules! print_numbers {
($($t:tt)*) => ($(
print!("{}:{} ", stringify!($t), $t::struct_number());
)*)
}
// should print:
// Baz:2 Bar:1 Foo:0
print_numbers!(Baz Bar Foo);
println!();
}
可以这样做:
- 首先计算所有论点
- 使用递归宏,因此可以计算
参数tail*
- 可以选择将结构列表存储在宏中,这样两个宏调用都不需要重复该列表
struct Foo {_var: bool}
struct Bar {_var: u8}
struct Baz {_var: i16}
trait NumberStruct {
fn struct_number() -> usize;
}
macro_rules! count_tts {
() => {0usize};
($_head:tt $($tail:tt)*) => {1usize + count_tts!($($tail)*)};
}
macro_rules! number_structs_impl {
() => {};
($head:tt $($tail:tt)*) => {
impl NumberStruct for $head {
fn struct_number() -> usize {
return STRUCT_NUM - (1 + count_tts!($($tail)*));
}
}
number_structs_impl!($($tail)*);
};
}
// avoid repeating same structs
macro_rules! apply_structs {
($macro_id:ident) => (
$macro_id! {
Foo
Bar
Baz
}
)
}
const STRUCT_NUM: usize = apply_structs!(count_tts);
apply_structs!(number_structs_impl);
fn main() {
// see if the numbers are correct
macro_rules! print_numbers {
($($t:tt)*) => ($(
print!("{}:{} ", stringify!($t), $t::struct_number());
)*)
}
// should print:
// Baz:2 Bar:1 Foo:0
print_numbers!(Baz Bar Foo);
println!();
}
注意:我发布这个答案是为了说明它的可能性,但是这是一个有点混乱的解决方案,因为它涉及到将宏传递给宏和两个调用,如果可以扩展递归宏,在每个递归中使用最后一个参数,这可以做得更干净,.定义编号实现的方法是使用递归宏。在本例中,可以通过计算尾随参数来创建唯一的数字 问题在于,如果第一个结构的数量最大,而最后一个结构的数量为零,则索引会反转 如果您只需要数字是唯一的,这无关紧要,但是在本例中,我希望每个结构索引与其传递给宏的顺序相匹配 可以使用递归宏反转输入参数 使用此宏,可以编写通用宏:
apply_args_reverse!(macro_name, arg1 arg2 arg3)
扩展为:
macro_name!(arg3 arg2 arg1)
当然,这本身不是很有用,但是如果参数不是直接编写的,而是作为参数传递的,那么它可能会很有用
这可用于创建一个宏,该宏按每个参数的数目展开,如下所示:
struct Foo {_var: bool}
struct Bar {_var: u8}
struct Baz {_var: i16}
trait NumberStruct {
fn struct_number() -> usize;
}
macro_rules! count_args_space {
() => {0_usize};
($_head:tt $($tail:tt)*) => {1_usize + count_args_space!($($tail)*)};
}
macro_rules! number_structs_impl {
(@single $t:tt $($tail:tt)*) => (
impl NumberStruct for $t {
fn struct_number() -> usize {
return count_args_space!($($tail)*);
}
}
);
() => {};
($head:tt $($tail:tt)*) => {
number_structs_impl!(@single $head $($tail)*);
number_structs_impl!($($tail)*);
};
}
macro_rules! apply_args_reverse {
($macro_id:tt [] $($reversed:tt)*) => {
$macro_id!($($reversed) *);
};
($macro_id:tt [$first:tt $($rest:tt)*] $($reversed:tt)*) => {
apply_args_reverse!($macro_id [$($rest)*] $first $($reversed)*);
};
// Entry point, use brackets to recursively reverse above.
($macro_id:tt, $($t:tt)*) => {
apply_args_reverse!($macro_id [ $($t)* ]);
};
}
// Note that both commands below work, and can be swapped to reverse argument order.
// number_structs_impl!(Foo Bar Baz);
apply_args_reverse!(number_structs_impl, Foo Bar Baz);
fn main() {
// see if the numbers are correct
macro_rules! print_numbers {
($($t:tt)*) => ($(
print!("{}:{} ", stringify!($t), $t::struct_number());
)*)
}
print_numbers!(Baz Bar Foo);
println!();
}
请注意以下声明:
number_structs_impl!(Foo Bar Baz);
。。。及
apply_args_reverse!(number_structs_impl, Foo Bar Baz);
。。。是可互换的,带注释的交换会颠倒分配给每个结构的数字顺序
注意:保持我的,虽然这更简洁,但它也更脆弱,容易出现难以解决的问题,因为宏扩展嵌套得很深(我至少在让它工作时发现了这一点)