Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating a compile-time constant integer from a literal string

I have a problem with non-portable code that works as intended on ARM RealView compiler, but VC++, GCC refuse to compile it and QAC++(a static analysis tool) issues a warning.

The problem

I have a system that needs to parse mnemonic identifiers in messages. The mnemonics are all three character 8-bit ASCII strings. To simplify and optimise parsing rather than performing string compare against the mnemonic string I pack the string into an 32-bit integer and perform an integer comparison.

Further in order to be able to use a switch/case rather than if-elseif chain, I have a macro that takes a literal string and generates the associated integer, which in ARM RealView is a compile time constant, but not in GCC x86/Linux or 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])))

This is then used on the ARM target code as follows:

switch( packed_mnemonic )
{
    case CONST_MNEMONIC(RST) :
        ...
        break ;

    case CONST_MNEMONIC(SSD) :
        ...
        break ;

    case CONST_MNEMONIC(DEL) :
        ...
        break ;

    default:
        ...
        break ;
}

The case label of course must be a compile-time constant, but apparently this is not the case for all compilers. The code is non-portable, and either I guess undefined or implementation defined behaviour, or just plain wrong!

The questions

The obvious portable solutions have disadvantages of efficiency and maintainability, so I have two questions:

  1. Why is this code not portable - what makes the macro not compile-time constant in some compilers?

  2. Is there a portable solution to generating the desired compile time constant from the mnemonic string?

like image 654
Clifford Avatar asked Jan 17 '14 12:01

Clifford


2 Answers

With C++11 you could use a constexpr function:

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]);
}
like image 88
Some programmer dude Avatar answered Oct 06 '22 09:10

Some programmer dude


It compiles fine here with gcc 4.8 and clang 3.4...

In C++11, you may use:

constexpr uint32_t CONST_MNEMONIC(const char (&s)[4])
{
    return (uint32_t(s[2]) << 16) | (uint32_t(s[1]) << 8) | uint32_t(s[0]);
}
like image 32
Jarod42 Avatar answered Oct 06 '22 10:10

Jarod42