6

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?

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • One problem you have is that when you use `#mn` in the macro, you are creating the string literal `"mn"`. To use the actual string literal passed to the macro drop the stringify operation. – Some programmer dude Jan 17 '14 at 12:53
  • Do you have 3 arrays `char RST[],SSD[],DEL[]` declared somewhere? A significant piece of your code is missing, making it hard to ananlyze the problem at hand. – barak manos Jan 17 '14 at 12:57
  • Looks like you found platform specificity in the ARM compiler with its willingness to compile this. The reason this isnt an ICE is becausse of the shift, I believe. In any case, I have done this before. When I get to work I'll try to find the code.. – John Dibling Jan 17 '14 at 12:57
  • Are you using C++11 or some other version? – John Dibling Jan 17 '14 at 13:06
  • which version are your compiler ? – Jarod42 Jan 17 '14 at 13:07
  • 1
    I think its the indexing into the string that's not portably compile time. IIRC doing bitwise shifts at compile time has pretty much always been standard. – Simon Jenkins Jan 17 '14 at 17:09
  • @JoachimPileborg: My apologies, the macros are not used that way - the "" are omitted from the parameter. I have edited the question. – Clifford Jan 17 '14 at 19:42
  • @barakmanos: Yes I have elided the code, but everything you need is there (although I have since corrected the macro usage example). There is no RST symbol etc., the preprocessor converts the macro parameters to string literals in `#mn`. – Clifford Jan 17 '14 at 19:45
  • @Jarod42: For what it is worth currently *RealView ARM Compiler toolchain v5.02*, but the code been used for a while over several versions, and has also worked on Microchip's C31 (dsPIC/PIC24) C compiler. – Clifford Jan 17 '14 at 20:03
  • @JohnDibling: a C++2003 solution would be preferable to conform to our coding standard - few embedded target compilers other than GCC support C++x11 – Clifford Jan 17 '14 at 20:05

2 Answers2

5

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]);
}
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 4
    So much easier in C++11 than it was in C++03. I struggled for days to build something that took you like 30 seconds. Ok, yeah. Maybe I'm a little jealous. – John Dibling Jan 17 '14 at 13:05
  • [Documentation](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0205j/Bgbdhgjd.html) indicates C++ 2003. However, this solution can be used on VC++ and GCC which we use for simulation and unit testing with a target specific conditional definition. Thanks. – Clifford Jan 17 '14 at 20:12
3

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]);
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302