如何从文字字节表达式构造const整数?

时间:2017-02-13 08:36:59

标签: rust byte const data-conversion

有没有办法从文字字节表达式构造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?

2 个答案:

答案 0 :(得分:3)

您可以通过索引到数组并将零件移位到正确的位置来为每种类型构建宏。您的u16的示例表达式是

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

您可以使用来自b"ID"的宏替换$e替换$e:expr

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

答案 1 :(得分: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 => {
    |             ^^^^^^^