我有一個巨大的四個字母標識符的列表。我想要一個枚舉恆定爲他們每個人。該常量的值應該是ASCII標識符u32
。是否可以使用宏來計算C樣式的`enum`值?
enum E {
Elem = my_macro!(Elem),
}
由於宏將被生成的AST簡單替換,編譯器將看到一個非常量表達式並引發錯誤。
這是可能的,還是我必須生成顯式寫出常量?
我有一個巨大的四個字母標識符的列表。我想要一個枚舉恆定爲他們每個人。該常量的值應該是ASCII標識符u32
。是否可以使用宏來計算C樣式的`enum`值?
enum E {
Elem = my_macro!(Elem),
}
由於宏將被生成的AST簡單替換,編譯器將看到一個非常量表達式並引發錯誤。
這是可能的,還是我必須生成顯式寫出常量?
的,是善良,但請記住,宏真的只允許你刪除苦差事,沒有真正增加新的功能。
首先寫入你想要的代碼沒有宏。這讓你看到什麼是可能的。例如,我的探索是這樣的:
#[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 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!("}}");
}
你會想要寫一個文件,而不是標準輸出,則包括從生產代碼生成的文件。構建腳本還允許您擁有一些定義所有名稱/代碼的外部文件,如果這很有價值。
感謝您的全面解答。 buildscript似乎是最乾淨的解決方案。雖然我希望能夠用語言來解決這個問題。 –
@BlankChisui *在語言中解決這個問題* - 構建腳本**是**的一部分。您仍然在編寫Rust代碼,這是Cargo的一流服務。大量的項目使用構建腳本。 – Shepmaster
也許考慮宏1.1或使用build.rs腳本(使用handlebars模板)來做codegen? – user25064
@ user25064您可以擴展您對Macros 1.1的幫助嗎?下面的答案已經提到了構建腳本。 – Shepmaster
既然你有枚舉元素作爲一個字符串是可能迭代遍歷字符與宏1.1? – user25064