2017-01-05 31 views
1

我有一個巨大的四個字母標識符的列表。我想要一個枚舉恆定爲他們每個人。該常量的值應該是ASCII標識符u32是否可以使用宏來計算C樣式的`enum`值?

enum E { 
    Elem = my_macro!(Elem), 
} 

由於宏將被生成的AST簡單替換,編譯器將看到一個非常量表達式並引發錯誤。

這是可能的,還是我必須生成顯式寫出常量?

+1

也許考慮宏1.1或使用build.rs腳本(使用handlebars模板)來做codegen? – user25064

+0

@ user25064您可以擴展您對Macros 1.1的幫助嗎?下面的答案已經提到了構建腳本。 – Shepmaster

+0

既然你有枚舉元素作爲一個字符串是可能迭代遍歷字符與宏1.1? – user25064

回答

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 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!("}}"); 
} 

你會想要寫一個文件,而不是標準輸出,則包括從生產代碼生成的文件。構建腳本還允許您擁有一些定義所有名稱/代碼的外部文件,如果這很有價值。

+0

感謝您的全面解答。 buildscript似乎是最乾淨的解決方案。雖然我希望能夠用語言來解決這個問題。 –

+0

@BlankChisui *在語言中解決這個問題* - 構建腳本**是**的一部分。您仍然在編寫Rust代碼,這是Cargo的一流服務。大量的項目使用構建腳本。 – Shepmaster

相關問題