Ищу МАКРОС, добавляющий два символа вместе для случаев переключения

#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 не принимает строковые литералы для использования в константных выражениях; что-то, что он должен.