Генерация целочисленного константы времени компиляции из литеральной строки
У меня проблема с непереносимым кодом, который работает так, как задумано на компиляторе 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 должна быть константой времени компиляции, но, очевидно, это не так для всех компиляторов. Код является непереносимым, и я предполагаю, что оно не определено или поведение, определяемое реализацией, или просто неверно!
Вопросы
Очевидные портативные решения имеют недостатки эффективности и ремонтопригодности, поэтому у меня есть два вопроса:
Почему этот код не переносим - что делает макрос не постоянным во время компиляции в некоторых компиляторах?
Существует ли переносимое решение для генерации желаемой постоянной времени компиляции из мнемонической строки?
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]);
}