Macros 在宏展开中使用参数编号?

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

将参数扩展到宏时,是否有方法将参数编号包含在宏中

下面是一个完整的示例,展示了如何使用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_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);
。。。是可互换的,带注释的交换会颠倒分配给每个结构的数字顺序


注意:保持我的,虽然这更简洁,但它也更脆弱,容易出现难以解决的问题,因为宏扩展嵌套得很深(我至少在让它工作时发现了这一点)