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

6

我有一个问题,涉及到非可移植的代码。在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标签必须是一个编译时常量,但显然并非所有编译器都是这样。该代码并不具备可移植性,可能产生未定义或实现相关行为,甚至是错误的!

问题

明显的便携式解决方案在效率和可维护性方面存在缺点,因此我有两个问题:

  1. 为什么这段代码不具备可移植性 - 什么使得宏在某些编译器中不是编译时常量?

  2. 是否有一种可移植的解决方案,可以从助记符字符串生成所需的编译时常量?


你遇到的一个问题是,当你在宏中使用#mn时,你创建了字符串字面量“mn”。要使用实际传递给宏的字符串字面量,请删除字符串化操作。 - Some programmer dude
你是否在某处声明了3个数组 char RST[],SSD[],DEL[]?你的代码缺少重要部分,这使得分析问题变得困难。 - barak manos
看起来你在ARM编译器中发现了平台特定性,因为它愿意进行编译。我认为这不是一个内部编译错误(ICE)的原因是因为移位操作。无论如何,我以前做过这个。等我上班后,我会尝试找到代码。 - John Dibling
你在使用C++11或其他版本吗? - John Dibling
1
我认为问题在于字符串的索引不是可移植的编译时操作。如果我没记错,在编译时进行位移操作几乎一直是标准的。 - Simon Jenkins
显示剩余5条评论
2个回答

5

使用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]);
}

4
在C++11中比在C++03中要容易得多。我为构建某个东西苦苦挣扎了数天,而你只用了30秒钟。好吧,也许我有点嫉妒。 - John Dibling
文档表明使用的是C++ 2003。然而,该解决方案可以在我们用于仿真和单元测试的VC++和GCC上使用,并带有特定目标条件定义。谢谢。 - Clifford

3

这段代码在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]);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接