Думаю, за этим кодом стояла следующая цель:
У нас есть несколько кнопок; в настоящее время только один, но со временем их может быть намного больше
Каждой кнопке нужна соответствующая глобальная переменная и значение в BoardButtonID
перечислении
Нам нужна функция, которая с заданным значением enum BoardButtonID
возвращает указатель на глобальную переменную кнопки
Мы хотим достичь всего этого, перечисляя все кнопки только один раз.
@ bolov показал, как расширяется код. Замечу, что можно добавить больше кнопок, просто изменив определение макроса BTN_TABLE
:
#define BTN_TABLE(X) X(BUTTON, PA1) \
X(ANOTHER_BUTTON, PA2) \
X(YET_ANOTHER_BUTTON, PA3) \
X(OH_GOD_NOT_ANOTHER_BUTTON_MAKE_THEM_STOP, PA4)
PA1, PA2, ...
фактически не используются в этой версии кода; возможно, они были бы использованы для чего-то позже.
Теперь вы можете увидеть эффект (я переформатировал вывод):
extern Button BUTTON;
extern Button ANOTHER_BUTTON;
extern Button YET_ANOTHER_BUTTON;
extern Button OH_GOD_NOT_ANOTHER_BUTTON_MAKE_THEM_STOP;
typedef enum {
BUTTON_ID,
ANOTHER_BUTTON_ID,
YET_ANOTHER_BUTTON_ID,
OH_GOD_NOT_ANOTHER_BUTTON_MAKE_THEM_STOP_ID,
NUM_BOARD_BUTTONS
} BoardButtonID;
static __forceinline Button* button_from_enum(BoardButtonID button_id) {
if (0)
return 0;
else if (button_id == BUTTON_ID)
return &BUTTON;
else if (button_id == ANOTHER_BUTTON_ID)
return &ANOTHER_BUTTON;
else if (button_id == YET_ANOTHER_BUTTON_ID)
return &YET_ANOTHER_BUTTON;
else if (button_id == OH_GOD_NOT_ANOTHER_BUTTON_MAKE_THEM_STOP_ID)
return &OH_GOD_NOT_ANOTHER_BUTTON_MAKE_THEM_STOP;
else
return 0;
}
И это делает его Понятно, зачем нужен начальный if
: расширение макроса в button_from_enum
не имеет возможности обрабатывать первый специально. Таким образом, он должен создать else if
для каждой кнопки, включая первую, и единственный способ сделать это действительным - это наличие if
в начале. У него должен быть тест, который всегда терпит неудачу, следовательно, 0
, и соответствующее ему предложение «then» не имеет значения, поскольку оно никогда не будет выполнено. return 0
там, возможно, был просто выбран, чтобы отключить предупреждение компилятора о возможности возврата функции без значения. Конечно, можно достичь return 0
в последнем предложении else
, и оно будет использоваться по умолчанию, если кто-то передаст значение, не соответствующее какой-либо кнопке.
Вы правы, что если вы поместите что-нибудь иначе между if
и else
все сломается.
Они могли бы определить его немного по-другому и использовать вместо него switch
, что было бы немного чище. Я не знаю, почему они этого не сделали; возможно, компилятор сгенерирует другой код, который им не понравился (например, таблица переходов, занимающая больше места для кода). . Возможно, им стоило подумать о написании сценария, который генерировал бы желаемый код из простого списка кнопок в текстовом файле.
Или они могли бы поместить Button
объекты в массив вместо того, чтобы настаивать на том, чтобы каждый из них имел свою собственную переменную. Это будет go хорошо с их enum
:
typedef enum {
BUTTON_ID,
ANOTHER_BUTTON_ID,
YET_ANOTHER_BUTTON_ID,
OH_GOD_NOT_ANOTHER_BUTTON_MAKE_THEM_STOP_ID,
NUM_BOARD_BUTTONS
} BoardButtonID;
Button all_the_buttons[NUM_BOARD_BUTTONS];
static __forceinline Button* button_from_enum(BoardButtonID button_id) {
if (button_id < NUM_BOARD_BUTTONS)
return &all_the_buttons[button_id];
else
return NULL;
}
Этот способ по-прежнему требует только один раз перечислить кнопки и не включает никаких макросов вообще.