Генерация целочисленного константы времени компиляции из литеральной строки

У меня проблема с непереносимым кодом, который работает так, как задумано на компиляторе ARM RealView, но VC++, GCC отказываются его компилировать, а QAC++(инструмент статического анализа) выдает предупреждение.

Эта проблема

У меня есть система, которая должна анализировать мнемонические идентификаторы в сообщениях. Мнемоника - это все трехсимвольные 8-битные строки ASCII. Чтобы упростить и оптимизировать синтаксический анализ, а не выполнять сравнение строк с мнемонической строкой, я упаковываю строку в 32-разрядное целое число и выполняю целочисленное сравнение.

Далее, чтобы иметь возможность использовать переключатель / регистр, а не цепочку 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. Существует ли переносимое решение для генерации желаемой постоянной времени компиляции из мнемонической строки?

2 ответа

Решение

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

Здесь он прекрасно компилируется с 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]);
}
Другие вопросы по тегам