Учитывая перечисление, которое назначается последовательно (т. е. первое значение равно 0, а последнее значение — это количество перечислений — 1), мы можем проверить, является ли значение допустимым значением перечисления, например:
enum e {FOO, BAR, BAZ};
int x = whatever;
if (x < 0 || x > 2)
/* x is not an explicitly defined member of the enum */
Можно ли определить макрос, который выполняет эту проверку и работает для всех последовательных перечислений?
Можно ли определить макрос, который выполняет эту проверку и работает для всех последовательных перечислений?
Нет. Язык Си не имеет отражения.
Обычно вы:
enum e {FOO, BAR, BAZ, E_MAX};
if (0 <= x && x < E_MAX)
enum xyz { FOO, BAR, BAZ, ..., xyz_n};
— хорошая и распространенная идиома C.
@chux-ReinstateMonica Понятно, это хорошая идея. Но теперь я получаю кучу предупреждений о необработанных случаях в операторах переключения, поэтому похоже, что невозможно использовать эту идиому и получить полезную диагностику компилятора, одновременно устраняя эти ложные срабатывания.
@You'reNotARobot "получил кучу предупреждений о необработанных случаях в операторах переключения" --> Хммм, у тебя есть default:
? Было бы интересно увидеть пример кода и настройки компилятора, вызвавшие это, в другом посте.
@chux-ReinstateMonica хорошая мысль, у меня нет варианта по умолчанию для этих функций. На самом деле причина моего вопроса была связана с этим: у меня есть некоторые функции, в которых недопустимое перечисление является ошибкой вызывающего абонента, и поэтому я даже не хочу тратить время на обработку случая по умолчанию, программа должна немедленно завершить работу. Поэтому мне нужен был макрос, подобный приведенному выше, чтобы я мог сделать assert(is_valid_enum_value(v))
в начале и тогда не нужно было обрабатывать значение регистра по умолчанию (и бонус в том, что в сборках выпуска я также могу отключить актив).
Вы можете использовать #define E_MAX BAZ
и #define IS_E(is_e) (0 < is_e && is_e <= E_MAX))
, что позволит проверкам перечисления переключателей продолжать работать (мне нравятся проверки переключателей). Или #define E_MAX (BAZ+1)
, если хотите, хотя я бы назвал это E_LIM
, потому что это не максимальное значение, которое enum e
может иметь.
@JohnBayko хорошая идея.
Я хотел знать, как сильно я смогу мучить C, чтобы это заработало. Я придумал это:
#define EE_VALUES { E1, E2, E3 }
enum ee_enum EE_VALUES;
#define IS_EE_ENUM(e) ((0 < e) && (e <= (sizeof((enum ee_enum[]) EE_VALUES)/sizeof(int))))
При этом размер гипотетического массива всех значений перечисления принимается за предел значения перечисления. Стандартный макрос размера массива:
sizeof(a)/sizeof(a[0])
Но перечисления представлены как целые числа, поэтому нет необходимости повторять массив.
Я не знаю, имеет ли это практическое применение, но вот оно.
Неа. Тип enum
известен компилятору. Препроцессор C ничего не знает о перечислениях.
Кроме того, «является допустимым значением перечисления» — это не то же самое, что «находится в пределах диапазона членов». Например,
BAR | BAZ
— допустимое значение, но больше, чемBAZ
.