有没有办法从文字字节表达式构造
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)) }
}
*请参阅相关问题:是否有与“stringify”宏等效的字节?
您可以通过索引数组并将各部分位移到正确的位置来为每种类型构建一个宏。 u16 的示例表达式是
((b"ID"[0] as u16) << 8) | (b"ID"[1] as u16)
您可以通过来自
b"ID"
的宏替换 $e
来替换 $e:expr
。
为了实现长度检查,可以插入一个无用的
*$e as [u8; 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 => {
| ^^^^^^^
使用较新的 Rust 版本,您可以这样做:
const IMA4_FOURCC: u32 = u32::from_be_bytes(*b"ima4");
在处理 FourCC 时非常有用。
如果愿意,您可以将其包装在宏中。