【问题标题】:How to construct const integers from literal byte expressions?如何从文字字节表达式构造 const 整数?
【发布时间】:2023-03-21 13:29:01
【问题描述】:

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

【问题讨论】:

  • 当对文字字节字符串使用位移时,我不知道如何实现。提问时请提供此类以前的工作
  • @ker,我开始这样做,但随后将其中一个问题标记为移至“代码审查”,我发现如果我发布示例代码,答案过于集中在挑选幼稚的代码上,而不是什么可能的,尤其是当我的方法一开始可能是错误的时候。不过我不介意发布示例。

标签: rust byte constants data-conversion


【解决方案1】:

您可以通过对数组进行索引并将部分位移到正确位置来为每种类型构建一个宏。您的 u16 的示例表达式是

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

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

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

【讨论】:

  • 不幸的是,使用此方法创建的常量不能用作匹配语句中的值(导致错误 E0080,请参阅我自己的答案中的注释)。
  • 正确,这是一个已经存在一段时间的夜间功能。您可以打开一个 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 => {
    |             ^^^^^^^

【讨论】:

  • 也许match 表达式而不是let 语句可以解决问题? match $w { w =&gt; .. }
  • 与此上下文中的几乎所有巧妙技巧一样,它给出:error[E0016]: blocks in constants are limited to items and tail expressions
  • 如果你将match 包装在一个块中。否则,你会得到......一个内部编译器错误(哎呀!)。然后我能想到的唯一解决方案是const fn(这是一个不稳定的功能),将表达式绑定到参数(const fn 还可以为您提供类型检查)。
猜你喜欢
  • 2013-02-04
  • 1970-01-01
  • 2021-12-25
  • 2012-04-19
  • 1970-01-01
  • 1970-01-01
  • 2013-10-08
  • 1970-01-01
  • 2010-11-28
相关资源
最近更新 更多