如何从文字字节表达式构造const整数?
有没有办法从字面字节表达式构造一个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?
您可以通过索引进入阵列和bitshifting零部件到正确位置:各类型的宏。您U16的例子表达
((b"ID"[0] as u16) << 8) | (b"ID"[1] as u16)
您可以通过宏替换$e
它来自一个$e:expr
更换b"ID"
。
要实现长度检查,可以插入一个无用的*$e as [u8; 2]
,如果类型不匹配,将无法编译。
不幸的是,使用此方法创建的常量不能用作匹配语句中的值(导致错误E0080,请参阅我自己的答案中的注释)。 – ideasman42
是正确的,这是一个已经存在一段时间的夜间功能。您可以打开RFC以使其稳定。 –
基于@ 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 => {
| ^^^^^^^
也许一个'match'表达式而不是'let'语句可以做到这一点? '匹配$ w {w => ..}' –
正如本文中几乎所有的巧妙技巧一样,它给出了:错误[E0016]:常量中的块仅限于项目和尾部表达式。 – ideasman42
这就是如果你在一个块中包装'match'。否则,你会得到一个内部编译器错误(oops!)。然后,我能想到的唯一解决方案是一个'const fn'(这是一个不稳定的特性),将表达式绑定到一个参数(并且'const fn'也给你类型检查)。 –
*我无法弄清楚如何使用时,位移上文字字节串*实现。请提供此前的工作,当您提出问题 –
@ker时,我开始这样做,但之后有一个问题被标记为“代码审查”,我发现如果我发布示例代码,答案会过分集中于分离天真的代码,而不是什么可能的,特别是当我的方法可能是错的时候。不过,我不介意举例说明。 – ideasman42