在宏扩展中使用参数号?

时间:2017-02-11 01:22:55

标签: macros rust

在扩展宏的参数时,有没有办法在宏中包含参数号

这是一个完整的示例,展示了如何使用特征将结构索引分配给结构。目前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!();
}

2 个答案:

答案 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,虽然这更简洁,但它也更脆弱,容易出现难以解决的问题,因为宏扩展会深深嵌套(我发现这个在让它至少工作的时候)