GCC включить перечисление, сохранить отсутствующее предупреждение, но использовать по умолчанию

Используя GCC, если вы switch по значению enum, а в одном из перечислений отсутствует оператор case, будет выдано предупреждение. Когда вы добавляете элемент default, предупреждение больше не будет излучаться, что имеет смысл в общем случае.

Есть ли способ использовать оператор default и по-прежнему иметь предупреждение, если не все значения enum охвачены? Поскольку моя функция может иметь дело с нечистыми вводами, я хотел бы осветить общий случай, но все же получить предупреждения компилятора о том, что отсутствует список перечислений.

В настоящее время я заканчиваю назначение значения по умолчанию после оператора switch.

Ответы

Ответ 1

-Wswitch-enum, но к несчастью, поддерживает только последняя версия.

(Вы могли бы, конечно, имитировать поведение, которое вы хотите, используя goto вне коммутатора и опустив значение по умолчанию, но я бы настоятельно советовал ему, что он уродлив, а кто-то другой, читающий ваш код, будет иметь опыт WTF.)

Ответ 2

Прочитав ссылку от "David Rodríguez - dribeas", я подумал, что было бы полезно обобщить перечисленные здесь варианты.

Есть два способа сделать это: либо повернуть сообщения о отсутствующих случаях перечисления для всех операторов switch, а затем отключить их для тех, которые вам не нужны, или оставить их по умолчанию и заставить ошибки для тех switch заявлений, которые вы действительно заботитесь.

Вариант 1: Предупреждения для всех, отметьте некоторые как тихие

Во-первых, добавьте -Wswitch-enum к вашим флагам компилятора, так что все инструкции switch, даже те, у которых есть предложение default, будут генерироваться предупреждения, если переименование не обрабатывается.

Затем для тех операторов switch, где вы хотите, чтобы случай default заботился о вещах и не хотел видеть предупреждения, заверните оператор switch следующим образом:

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum"

switch () {
    ...
}

#pragma GCC diagnostic pop

Это временно отключит флаг -Wswitch-enum (скрывая предупреждения о недостающих случаях перечисления) только для этого оператора case.

Вариант 2: предупреждать только о том, чтобы сделать это

Поскольку поведение GCC по умолчанию заключается в том, чтобы скрыть предупреждения, когда существует предложение default, для этого параметра флаги компилятора не нужно изменять.

Вместо этого для тех операторов switch, которые содержат предложение default, но вы все еще хотите видеть предупреждения о недостающих случаях перечисления, оберните switch следующим образом:

#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-Wswitch-enum"

switch () {
    ...
}

#pragma GCC diagnostic pop

Это временно включает флаг -Wswitch-enum между строками push и pop, в результате чего сообщения о отсутствующих случаях перечисления отображаются даже при наличии предложения default. Вы можете изменить слово warning на error, если вы хотите, чтобы компиляция завершилась с ошибкой.

Ответ 3

К сожалению, на сегодняшний день ни gcc, ни llvm не могут обнаружить, что вы не сравниваете все значения enum в switch, если вы включаете в себя переключатель default.

Ответ 4

Я бы сказал, что проблема больше на уровне enum.

Что я имею в виду, так это то, что вы должны сначала подтвердить свой ввод (т.е. убедиться, что он действительно отображается в реальное перечисление) и только после проверки, следует использовать перечисление, и в этом случае значение по умолчанию становится избыточным.

Чтобы проверить ввод, простым решением, которое я использую, является создание перечисления через макрос, который также автоматически генерирует функции преобразования: от/до string, от int (или что-то еще).

Например:

DEFINE_ENUM_DETAILED(SomeEnum, int, (Foo, 0, "Foo")(Bar, 1, "Bar"));

Может генерировать:

struct SomeEnum {
  enum { Foo = 0, Bar = 1 } type;

  type FromString(std::string const& s) {
    if (s == "Foo") { return Foo; }
    if (s == "Bar") { return Bar; }
    assert(0 && "SomeEnum::FromString - unknown value");
  }

  std::string ToString(type e) {
    switch(e) {
    case Foo: return "Foo";
    case Bar: return "Bar";
    }
  }

  type FromIntegral(int i) {
    switch(i) {
    case Foo: return Foo;
    case Bar: return Bar;
    }
    assert(0 && "SomeEnum::FromIntegral- unknown value");
  }
};

Это единственный способ, который я нашел, чтобы сгенерировать это легко (хотя преобразование строк здесь немного упрощено).

Другим решением будет использование script для генерации исходного кода из альтернативного файла.

РЕДАКТИРОВАТЬ: Операция и проверка коммутатора

Простой ответ (например, я сделал выше), чтобы программа выходила из коммутатора вместо использования предложения по умолчанию. Это возможно, если нормальный поток (при попадании в case) не выйдет из переключателя.

switch(event) {
case Foo: {
  // bla
  return 0;
}
case Bar: {
  return 0;
}
}

unreachable("should never have got there");

Ответ 5

Одна из причин заключается в том, что вы должны иметь возможность писать код без предупреждения, не нарушая вас. Если, например, у вас есть перечисление со 100 константами, вам нужно будет перечислить каждый из них в каждом операторе switch, даже если вам нужно только осмотреть несколько из них, если предупреждение должно работать так, как вы предложили.