在扩展宏的参数时,有没有办法在宏中包含参数号
这是一个完整的示例,展示了如何使用特征将结构索引分配给结构。目前struct_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!();
}
答案 0 :(得分:0)
可以这样做:
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!();
}
注意:我发布这个答案是为了表明它可能,但是它有点乱,因为它涉及将宏传递给宏和两个调用,如果可以扩展递归宏,这可以更干净地完成最后参数每次递归see related question。
答案 1 :(得分:0)
定义编号实现的方法是使用递归宏。 counting arguments可以创建唯一的数字,在这种情况下计算尾随参数。
这个问题是,索引是相反的,其中第一个结构具有最大数字,最后一个结构为零。
如果你只需要数字是唯一的,那就不重要了,但是在这种情况下我希望每个结构索引与它传递给宏的顺序相匹配。
可以使用递归宏see this example来反转输入参数。
使用此宏,可以编写通用宏:
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);
...是可以互换的,被评论的交换会反转分配给每个结构的数字的顺序。
注意:保留我的other answer,虽然这更简洁,但它也更脆弱,容易出现难以解决的问题,因为宏扩展会深深嵌套(我发现这个在让它至少工作的时候)。