Есть ли способ не предупреждать об отсутствии констант перечисления COUNT в переключателе в gcc?

Если переключатель с аргументом типа enum пропускает некоторые константы и не имеет ветки по умолчанию, параметр gcc -Wswitch вызывает предупреждение вроде

warning: enumeration value 'WHATEVER' not handled in switch

Тем не менее, многие наши переключатели похожи на:

enum foo {
    FOO_ONE,
    FOO_TWO,
    FOO_COUNT
};

где FOO_COUNT никогда не появляется как значение, но используется для определения количества значений, которые определены и могут появиться в переменной. Потому что мы индексируем массив со значением перечисления или упаковываем его побитно, и нам нужно проверить, подойдет ли он или что-то в этом роде. Таким образом, перечисление, которое обрабатывает все значения, должно не включать эту одну константу. Есть ли способ сохранить это предупреждение, но избежать его для таких особых значений? т.е.

switch(foo) {
    case FOO_ONE:
        anything;
};

должен дать предупреждение, но:

switch(foo) {
    case FOO_ONE:
        anything;
    case FOO_TWO:
        anything_else;
}

не следует.


person Jan Hudec    schedule 24.08.2012    source источник
comment
FWIW Я думаю, проблема в том, что ваше перечисление находится в состоянии греха. Он содержит значение, которое (поскольку вам не нужно обрабатывать его в переключателе) на самом деле не является возможным значением типа. Другими словами, никто никогда не должен писать foo f = FOO_COUNT;, но вы дали им средства для этого. Я полагаю, вы не хотите писать static const int FOO_COUNT = FOO_TWO+1; после перечисления или даже #define FOO_COUNT (FOO_TWO+1) внутри перечисления из-за риска того, что вы забудете обновить его при добавлении FOO_THREE?   -  person Steve Jessop    schedule 24.08.2012
comment
Другой вариант, лишь немного нарушающий комментарий Стива: добавить FOO_LAST = FOO_TWO в перечисление. Это не добавит новое значение в перечисление, и вы избежите загрязнения пространства имен препроцессора (а в С++ FOO_LAST будет в правильном пространстве имен).   -  person user2224044    schedule 08.04.2021


Ответы (3)


Лично я предпочитаю другой подход: создание enum с помощью макроса для настройки счетчика.

GENERATE_ENUM(foo, (FOO_ONE)(FOO_TWO))

будет производить:

enum foo {
    FOO_ONE,
    FOO_TWO
};

inline size_t size(enum foo) { return 2; }

И, таким образом, мое перечисление не содержит предупреждений.

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

person Matthieu M.    schedule 24.08.2012
comment
какой синтаксис макроса вы используете для автоматического подсчета количества значений перечисления, введенных в ваш макрос GENERATE_ENUM()? - person ryeager; 24.03.2016
comment
@ryeager: я использую библиотеку препроцессора Boost, а точнее BOOST_PP_SEQ_SIZE макрос, который расширяется до размера последовательности. - person Matthieu M.; 24.03.2016

Если вы хотите по-прежнему получать предупреждения для остальных, единственное, что я могу придумать, это создать случай:

switch (foo) {
...
case FOO_COUNT: //empty
}
person David Rodríguez - dribeas    schedule 24.08.2012
comment
Либо это, либо пустое предложение default:. Однако не могу придумать каких-либо чрезвычайно очевидных причин предпочесть одно другому... - person twalberg; 24.08.2012
comment
@twalberg: причина предпочтения case FOO_COUNT: вместо default: заключается в том, чтобы по-прежнему получать предупреждение, когда вы забываете включить FOO_TWO в переключатель (или когда вы позже добавляете FOO_THREE в перечисление и забываете обновить переключатель). default: означает обрабатывать все, что мы хотим, это обрабатывать только специальное значение. - person Steve Jessop; 24.08.2012
comment
@SteveJessop Хороший вопрос ... Я больше думал о коде, сгенерированном в двух случаях, который должен быть почти одинаковым. Не думал о случае по умолчанию, который может скрывать дополнительные предупреждения. - person twalberg; 24.08.2012

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

switch(value){
    case FOO_ONE:
        break;
    default:
        break;
}

Вы также можете комбинировать его с макросом, чтобы вы могли «включить» предупреждения из одной точки вашего кода:

#define m_ignore_switch_case_warnings default: break
//and define it like this to enable the warnings again
#define m_ignore_switch_case_warnings

switch(value){
    case FOO_ONE:
        break;

    m_ignore_switch_case_warnings;
}

Вы сможете «отключить» предупреждения для любого типа switch, и вам не придется обновлять свой код switch в случае изменения вашего перечисления FOO_COUNT или если у вас разные имена для ваших переменных count.

В противном случае вас отсылают к сообщению David Rodríguez - dribeas

person xQuare    schedule 24.08.2012
comment
Проблема с этим подходом заключается в том, что он не будет предупреждать, если вы забудете какой-либо другой счетчик, о чем Ян, кажется, все еще хочет получить предупреждение. - person David Rodríguez - dribeas; 24.08.2012