#c #macros #static #switch-statement #character
#c #макросы #статический #switch-statement #символ
Вопрос:
Я работаю над старым сетевым движком, и тип пакета, отправляемого по сети, состоит из 2 байт.
Это более или менее понятная для человека форма, например, «LO» означает логин.
В части, которая считывает данные, есть огромный переключатель, например:
short sh=(((int)ad.cData[p])<<8) ((int)ad.cData[p 1]);
switch(sh)
{
case CMD('M','D'):
..some code here
break
где CMD — это определение:
#define CMD(a,b) ((a<<8) b)
Я знаю, что есть лучшие способы, но просто немного очистить, а также иметь возможность более легко искать тег (скажем, «LO») (и не искать разные типы «‘L’, ‘O'» или «‘L’, ‘O'» илислучайные «‘L’, ‘O'» <- пробелы затрудняют поиск) Я попытался создать МАКРОС для коммутатора, чтобы я мог использовать «LO» вместо определения, но я просто не могу заставить его скомпилироваться.
Итак, вот вопрос: как изменить #define на макрос, который я могу использовать вместо этого:
case CMD("MD"):
..some code here
break
Это началось как небольшая подзадача, чтобы немного облегчить жизнь, но теперь я не могу выбросить это из головы, спасибо за любую помощь!
Приветствия!
[править] Код работает, это мир, который ошибается! т.е.. В Visual Studio 2010 есть ошибка, связанная с этим. Неудивительно, что я порезал зубы на нем.
Комментарии:
1. Обязательно ли это должен быть макрос? Я бы подумал
constexpr
, что подходящим вариантом будет функция, хотя для этого может потребоваться очень свежий компилятор.2. Это не обязательно должен быть макрос, если есть что-то еще, что может сработать, но я на MSVC 2010, поэтому нет constexpr… В любом случае спасибо!
Ответ №1:
Решение на основе макросов
Строковый литерал на самом деле является экземпляром char const[N]
where N
— длина строки, включая завершающий нулевой байт. Имея это в виду, вы можете легко получить доступ к любому символу в строковом литерале, используя string-literal[idx]
, чтобы указать, что вы хотите прочитать символ, сохраненный со смещением idx
.
#define CMD(str) ((str[0]<<8) str[1])
CMD("LO") => (("LO"[0]<<8) "LO"[1]) => (('L'<<8) '0')
Однако вы должны иметь в виду, что ничто не мешает вам использовать приведенный выше макрос со строкой, которая короче строки length 2
, что означает, что вы можете столкнуться с неопределенным поведением, если попытаетесь прочитать смещение, которое на самом деле недействительно.
РЕКОМЕНДУЕТСЯ: C 11, используйте функцию constexpr
Вы могли бы создать функцию, используемую в константных выражениях (и с этим, в метках регистра), с параметром ссылки на const char[3], который является «реальным» типом вашего строкового литерала «FO».
constexpr short cmd (char const(amp;ref)[3]) {
return (ref[0]<<8) ref[1];
}
int main () {
short data = ...;
switch (data) {
case cmd("LO"):
...
}
}
C 11 и определяемые пользователем литералы
В C 11 нам была предоставлена возможность определять пользовательские литералы. Это значительно упростит обслуживание и интерпретацию вашего кода, а также сделает его более безопасным в использовании:
#include <stdexcept>
constexpr short operator"" _cmd (char const * s, unsigned long len) {
return len != 2 ? throw std::invalid_argument ("") : ((s[0]<<8) s[1]);
}
int main () {
short data = ...;
switch (data) {
case "LO"_cmd:
...
}
}
Значение, связанное с меткой регистра, должно быть получено с помощью константного выражения. Может показаться, что приведенное выше может вызвать исключение во время выполнения, но поскольку метка регистра является постоянным выражением, компилятор должен иметь возможность оценивать "LO"_cmd
во время трансляции.
Если это невозможно, например "FOO"_cmd
, компилятор выдаст диагностическое сообщение о том, что код неверно сформирован.
Комментарии:
1. Спасибо за отличный ответ, к сожалению, я на MSVC 2010 и, следовательно, не имею доступа к C 11, а (старый и небезопасный) код — это то, что у меня не работает. Это выдает мне следующую ошибку: ошибка C2051: выражение регистра не является постоянным
2. @Valmond вы хотите сказать, что решение макроса не работает?
3. Да, он не работает и выдает ошибку C2051. Это похоже на вызов функции (и, следовательно, MSVC не может предварительно оценить значение для выборки / случая), ну, в любом случае, это моя интерпретация ошибки… Возможно, это переключатель компилятора или что-то еще, что мне нужно изменить, но это не работает.
4. @Valmond другими словами; msvc делает это неправильно, текущий ответ в настоящее время сохраняется. если у вас есть вопросы относительно решения проблемы очевидной ошибки в msvc , вам следует задать другой вопрос.
5. @Valmond кроме того, это не связано с тем, что макрос «ведет себя как вызов функции», msvc не принимает строковые литералы для использования в константных выражениях; что-то, что он должен.