A #define в C с тремя точками

#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__)) #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__)) 

Это определение для этих двух макросов; позже в коде LOGW и LOGW используются таким образом

 LOGI("accelerometer: x=%fy=%fz=%f", event.acceleration.x, event.acceleration.y, event.acceleration.z); 

и таким образом

 LOGW("Unable to eglMakeCurrent"); 

Поскольку я стараюсь избегать сложных макросов и #define в целом, я не могу получить то, что на самом деле означает этот макрос. Какова роль трехзначной нотации здесь? Что изменит этот #define позже в коде?

Очевидно, я знаю, что три точки используются для указания и неопределенного количества аргументов, но я не знаю, как читать эту ситуацию.

В стандарте C99 вводились переменные macros , т. Е. Функциональные macros, которые могут принимать переменное количество аргументов.

Цитируя последний проект стандарта C, раздел 6.10.3:

Если идентификатор-список в определении макроса не заканчивается эллипсисом, количество аргументов (включая те аргументы, которые не состоят из токенов предварительной обработки) при вызове функционально-подобного макроса, должно равняться числу параметров в определении макроса. В противном случае в вызове должно быть больше аргументов, чем в определении макроса (кроме ... ). Должен существовать a) токен предварительной обработки, который завершает вызов.

Идентификатор __VA_ARGS__ должен присутствовать только в списке __VA_ARGS__ функционально-подобного макроса, который использует нотацию многоточия в параметрах.

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

И в следующем подразделе:

Идентификатор __VA_ARGS__ который встречается в списке замены, обрабатывается так, как если бы он был параметром, а переменные аргументы должны составлять токены предварительной обработки, используемые для его замены.

Таким образом, вы можете вызывать LOGW или LOGW с таким количеством аргументов, сколько хотите, и все они будут расширены в месте, указанном в определении, по ссылке __VA_ARGS__ .