2017-02-13 11 views
3

有没有办法从字面字节表达式构造一个const整数,或者使用字节字符串或构造整数的宏?如何从文字字节表达式构造const整数?

例如:

const MY_ID: u16 = u16_code!(ID); 
const MY_WORD: u32 = u32_code!(WORD); 
const MY_LONG: u64 = u64_code!(LONGWORD); 

或者类似的东西,传递b"ID"而不是ID*

当错误的字符数也被传递时,它应该编译失败,这是我无法弄清楚在字面字节串中使用位移时如何实现的。


下面是一个简单的例子,它在基本级别上工作,但未能确保大小正确的参数。

// const MY_ID: u16 = u16_code!(b"ID"); 
#[cfg(target_endian = "little")] 
macro_rules! u16_code { 
    ($w:expr) => { ((($w[0] as u16) << 0) | (($w[1] as u16) << 8)) } 
} 
#[cfg(target_endian = "big")] 
macro_rules! u16_code { 
    ($w:expr) => { ((($w[1] as u16) << 0) | (($w[0] as u16) << 8)) } 
} 

*请参阅相关的问题:Is there a byte equivalent of the 'stringify' macro?

+0

*我无法弄清楚如何使用时,位移上文字字节串*实现。请提供此前的工作,当您提出问题 –

+1

@ker时,我开始这样做,但之后有一个问题被标记为“代码审查”,我发现如果我发布示例代码,答案会过分集中于分离天真的代码,而不是什么可能的,特别是当我的方法可能是错的时候。不过,我不介意举例说明。 – ideasman42

回答

3

您可以通过索引进入阵列和bitshifting零部件到正确位置:各类型的宏。您U16的例子表达

((b"ID"[0] as u16) << 8) | (b"ID"[1] as u16) 

您可以通过宏替换$e它来自一个$e:expr更换b"ID"

要实现长度检查,可以插入一个无用的*$e as [u8; 2],如果类型不匹配,将无法编译。

+0

不幸的是,使用此方法创建的常量不能用作匹配语句中的值(导致错误E0080,请参阅我自己的答案中的注释)。 – ideasman42

+0

是正确的,这是一个已经存在一段时间的夜间功能。您可以打开RFC以使其稳定。 –

2

基于@ ker的的建议,下面是基于固定大小的字节串创建的常数标识符便携宏:

警告:但是也有一些不是很明显这些常量一些限制(见下面的注意事项)。

以下宏支持:

const MY_ID: u16 = u16_code!(b"ID"); 
const MY_WORD: u32 = u32_code!(b"WORD"); 
const MY_LONG: u64 = u64_code!(b"LONGWORD"); 

实现:

#[cfg(target_endian = "little")] 
#[macro_export] 
macro_rules! u16_code { 
    ($w:expr) => { 
     ((($w[0] as u16) << 0) | 
     (($w[1] as u16) << 8) | 
     ((*$w as [u8; 2])[0] as u16 * 0)) 
    } 
} 
#[cfg(target_endian = "big")] 
#[macro_export] 
macro_rules! u16_code { 
    ($w:expr) => { 
     ((($w[1] as u16) << 0) | 
     (($w[0] as u16) << 8) | 
     ((*$w as [u8; 2])[0] as u16 * 0)) 
    } 
} 

#[cfg(target_endian = "little")] 
#[macro_export] 
macro_rules! u32_code { 
    ($w:expr) => { 
     ((($w[0] as u32) << 0) | 
     (($w[1] as u32) << 8) | 
     (($w[2] as u32) << 16) | 
     (($w[3] as u32) << 24) | 
     ((*$w as [u8; 4])[0] as u32 * 0)) 
    } 
} 
#[cfg(target_endian = "big")] 
#[macro_export] 
macro_rules! u32_code { 
    ($w:expr) => { 
     ((($w[3] as u32) << 0) | 
     (($w[2] as u32) << 8) | 
     (($w[1] as u32) << 16) | 
     (($w[0] as u32) << 24) | 
     ((*$w as [u8; 4])[0] as u32 * 0)) 
    } 
} 

#[cfg(target_endian = "little")] 
#[macro_export] 
macro_rules! u64_code { 
    ($w:expr) => { 
     ((($w[0] as u64) << 0) | 
     (($w[1] as u64) << 8) | 
     (($w[2] as u64) << 16) | 
     (($w[3] as u64) << 24) | 
     (($w[4] as u64) << 32) | 
     (($w[5] as u64) << 40) | 
     (($w[6] as u64) << 48) | 
     (($w[7] as u64) << 56) | 
     ((*$w as [u8; 8])[0] as u64 * 0)) 
    } 
} 
#[cfg(target_endian = "big")] 
#[macro_export] 
macro_rules! u64_code { 
    ($w:expr) => { 
     ((($w[7] as u64) << 0) | 
     (($w[6] as u64) << 8) | 
     (($w[5] as u64) << 16) | 
     (($w[4] as u64) << 24) | 
     (($w[3] as u64) << 32) | 
     (($w[2] as u64) << 40) | 
     (($w[1] as u64) << 48) | 
     (($w[0] as u64) << 56) | 
     ((*$w as [u8; 8])[0] as u64 * 0)) 
    } 
} 

注1),检查需要与恒定的,因为分开地进行或运算的尺寸线语句在常量表达式中不受支持(E0016)。

我也宁愿在一个宏内使用if cfg!(target_endian = "big"),但常数的相同限制阻止它。

注2)有可能使用这些宏用于非恒定的输入,其中,所述参数可以被实例化的每个字节(和可能为尺寸完整性检查)的问题。我看着分配一个变量,但这也会导致错误E0016

注3)尽管Rust允许将这些值声明为const,但它们不能在match语句中使用。

如:

error[E0080]: constant evaluation error 
    --> src/mod.rs:112:23 
    | 
112 | const MY_DATA: u32 = u32_code!(b"DATA"); 
    |      ^^^^^^^^^^^^^^^^^^ the index operation on const values is unstable 
    | 
note: for pattern here 
    --> src/mod.rs:224:13 
    | 
224 |    MY_DATA => { 
    |    ^^^^^^^ 
+0

也许一个'match'表达式而不是'let'语句可以做到这一点? '匹配$ w {w => ..}' –

+0

正如本文中几乎所有的巧妙技巧一样,它给出了:错误[E0016]:常量中的块仅限于项目和尾部表达式。 – ideasman42

+0

这就是如果你在一个块中包装'match'。否则,你会得到一个内部编译器错误(oops!)。然后,我能想到的唯一解决方案是一个'const fn'(这是一个不稳定的特性),将表达式绑定到一个参数(并且'const fn'也给你类型检查)。 –

相关问题