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

wtlkbnrh  于 12个月前  发布在  其他
关注(0)|答案(3)|浏览(78)

有没有一种方法可以从一个字节表达式构造一个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?

r6l8ljro

r6l8ljro1#

你可以为每种类型建立一个宏,方法是在数组中建立索引,并将部分移位到正确的位置。

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

字符串
您可以使用来自$e:expr的宏替换$e来替换b"ID"
为了实现长度检查,可以插入一个无用的*$e as [u8; 2],如果类型不匹配,它将无法编译。

aydmsdu9

aydmsdu92#

根据@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"),但常量的相同限制阻止了它。

**Note 2)**使用这些宏进行非常量输入可能会出现问题,其中参数可以为每个字节示例化 (可能还有大小的健全性检查)。我研究了分配变量,但这也导致错误E0016
**Note 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 => {
    |             ^^^^^^^

nkcskrwz

nkcskrwz3#

使用较新的Rust版本,您可以执行以下操作:

const IMA4_FOURCC: u32 = u32::from_be_bytes(*b"ima4");

字符串
这在处理FourCC时非常有用。
如果你想的话,你可以把它 Package 在一个宏中。

相关问题