我有四个字母标识符的巨大列表。我希望每个人都有一个枚举常量。常量的值应为ASCII标识符u32
。
enum E {
Elem = my_macro!(Elem),
}
由于宏将简单地被生成的AST替换,编译器将看到非常量表达式并引发错误。
这是可能的,还是我必须生成明确写出常量?
答案 0 :(得分:4)
有点,是的,但请记住,宏确实只允许您删除苦差事,而不是真正添加新功能。
首先编写您想要的代码不带宏。这可以让你看到可能的。例如,我的探索是这样的:
#[repr(u32)]
enum Foo {
Start = (1 << 24 | 2 << 16 | 3 << 8 | 4) as u32,
Start2 = ((b'1' as u32) << 24 | 2 << 16 | 3 << 8 | 4) as u32,
// the index operation on const values is unstable
//Start3 = ((b"1"[0] as u32) << 24 | 2 << 16 | 3 << 8 | 4) as u32,
}
可悲的是,我们不能使用最后一种形式作为常量索引不是一个常量表达式(还)。据我所知,我们所能做的最好的是Start2
。接下来,重复几次模式以查看冗余发生的位置:
#[repr(u32)]
enum Foo {
Start = ((b'S' as u32) << 24 | (b'T' as u32) << 16 | (b'R' as u32) << 8 | (b'T' as u32)) as u32,
End = ((b' ' as u32) << 24 | (b'E' as u32) << 16 | (b'N' as u32) << 8 | (b'D' as u32)) as u32,
}
现在您准备创建一个宏:
macro_rules! tagged_ascii_headers {
(enum $name:ident {
$($var:ident = $v1:expr, $v2:expr, $v3:expr, $v4:expr,)*
}) => {
#[repr(u32)]
enum $name {
$($var = (($v1 as u32) << 24 | ($v2 as u32) << 16 | ($v3 as u32) << 8 | $v4 as u32),)*
}
}
}
tagged_ascii_headers! {
enum Foo {
Start = b'S', b'T', b'R', b'T',
End = b' ', b'E', b'N', b'D',
}
}
然后你可以使用宏语法来找到看起来不错的东西。我下到了
macro_rules! tagged_ascii_headers {
(enum $name:ident {
$($var:ident = $v1:tt $v2:tt $v3:tt $v4:tt,)*
}) => {
#[repr(u32)]
enum $name {
$($var = (($v1 as u32) << 24 | ($v2 as u32) << 16 | ($v3 as u32) << 8 | $v4 as u32),)*
}
}
}
tagged_ascii_headers! {
enum Foo {
Start = 'S' 'T' 'R' 'T',
End = ' ' 'E' 'N' 'D',
}
}
这有点好,但最终您可能需要更多的持续评估才能获得。 如果数组可以编入索引,您可以演变为类似
tagged_ascii_headers! {
enum Foo {
Start = b"STRT",
End = b" END",
}
}
因为宏只会被生成的AST替换
这是真的
编译器将看到非常量表达式
这是半真的。例如,这编译得很好:
macro_rules! foo {
() => { 42 }
}
enum Foo {
Start = foo!(),
}
实际上,宏与const-ness无关,而是关于宏扩展到的所有内容。
您也可以转到构建脚本:
const THINGS: &'static [(&'static str, &'static [u8; 4])] = &[
("Start", b"STRT"),
("End", b" END"),
];
fn main() {
println!("#[repr(u32)]");
println!("enum Foo {{");
for &(name, code) in THINGS {
let code = (code[0] as u32) << 24 |
(code[1] as u32) << 16 |
(code[2] as u32) << 8 |
code[3] as u32;
println!(" {} = {},", name, code);
}
println!("}}");
}
您希望将其写入文件而不是标准输出,然后包含生产代码中生成的文件。构建脚本还允许您拥有一些定义所有名称/代码的外部文件,如果这些文件有价值的话。