从文字字符串生成编译时常量整数

Posted

技术标签:

【中文标题】从文字字符串生成编译时常量整数【英文标题】:Generating a compile-time constant integer from a literal string 【发布时间】:2014-01-17 12:49:04 【问题描述】:

我对在 ARM RealView 编译器上按预期工作的不可移植代码有疑问,但 VC++、GCC 拒绝编译它并且 QAC++(一种静态分析工具)发出警告。

问题

我有一个系统需要解析消息中的助记符标识符。助记符都是三个字符的 8 位 ASCII 字符串。为了简化和优化解析而不是对助记字符串执行字符串比较,我将字符串打包成一个 32 位整数并执行整数比较。

为了能够使用 switch/case 而不是 if-elseif 链,我有一个宏,它采用文字字符串并生成相关的整数,在 ARM RealView 中是编译时间常数,但在GCC x86/Linux 或 VC++/Windows:

// Note:  Do not change C cast to static_cast because compiler complains when used in switch/case
#define CONST_MNEMONIC( mn ) ((uint32_t)(((#mn)[2]<<16)|((#mn)[1]<<8)|((#mn)[0])))

然后在 ARM 目标代码上使用如下:

switch( packed_mnemonic )

    case CONST_MNEMONIC(RST) :
        ...
        break ;

    case CONST_MNEMONIC(SSD) :
        ...
        break ;

    case CONST_MNEMONIC(DEL) :
        ...
        break ;

    default:
        ...
        break ;

case 标签当然必须是编译时常量,但显然并非所有编译器都如此。代码是不可移植的,我猜是未定义或实现定义的行为,或者完全错误!

问题

显而易见的可移植解决方案存在效率和可维护性的缺点,所以我有两个问题:

    为什么这段代码不可移植 - 是什么让宏在某些编译器中不是编译时常量?

    是否有可移植的解决方案来从助记符字符串生成所需的编译时间常数?

【问题讨论】:

您遇到的一个问题是,当您在宏中使用#mn 时,您正在创建字符串文字"mn"。要使用传递给宏的实际字符串文字,请删除 stringify 操作。 您是否在某处声明了 3 个数组 char RST[],SSD[],DEL[]?缺少重要的一段代码,因此很难分析手头的问题。 看起来您在 ARM 编译器中发现了平台特异性,并且愿意编译它。我相信,这不是 ICE 的原因是因为这种转变。无论如何,我以前做过。当我开始工作时,我会尝试找到代码.. 您使用的是 C++11 还是其他版本? 我认为它对字符串的索引不是可移植的编译时间。 IIRC 在编译时进行位移几乎一直是标准的。 【参考方案1】:

对于 C++11,您可以使用 constexpr 函数:

constexpr int CONST_MNEMONIC(const char* s)

    return (static_cast<int>(s[2]) << 16) +
           (static_cast<int>(s[1]) <<  8) +
            static_cast<int>(s[0]);

【讨论】:

在 C++11 中比在 C++03 中容易得多。我花了好几天的时间来构建一个只需要 30 秒的东西。好的,是的。也许我有点嫉妒。 Documentation 表示 C++ 2003。但是,此解决方案可用于 VC++ 和 GCC,我们使用目标特定条件定义进行模拟和单元测试。谢谢。【参考方案2】:

它在这里用 gcc 4.8 和 clang 3.4 编译得很好......

在 C++11 中,你可以使用:

constexpr uint32_t CONST_MNEMONIC(const char (&s)[4])

    return (uint32_t(s[2]) << 16) | (uint32_t(s[1]) << 8) | uint32_t(s[0]);

【讨论】:

以上是关于从文字字符串生成编译时常量整数的主要内容,如果未能解决你的问题,请参考以下文章

枚举使用编译时常量转换为字符串

Java final 字段编译时常量表达式

如何从枚举在 Kotlin 中创建编译时常量?

java中,运行时生成的字符串放在啥地方了呢?@大拿们

Initialiser 元素不是编译时常量

.NET DateTime类型变量作为参数时设置默认值