A #define in C with three dots

Ken picture Ken · Sep 15, 2012 · Viewed 9.2k times · Source
#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__))

This is definition for these 2 macros; later in the code LOGI and LOGW are used this way

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

and this way

LOGW("Unable to eglMakeCurrent");

Since I try to avoid complex macros and #define in general, I can't get what this macro actually means. What is the role for the 3 dots notation here? What does this #define change later in the code?

Obviously I know that the 3 dots are used to indicate and indefinite amount of arguments, but I don't know how to read this situation.

Answer

Keith Thompson picture Keith Thompson · Sep 15, 2012

The C99 standard introduced variadic macros, i.e., function-like macros that can take a variable number of arguments.

Quoting the latest draft of the C standard, section 6.10.3:

If the identifier-list in the macro definition does not end with an ellipsis, the number of arguments (including those arguments consisting of no preprocessing tokens) in an invocation of a function-like macro shall equal the number of parameters in the macro definition. Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...). There shall exist a ) preprocessing token that terminates the invocation.

The identifier __VA_ARGS__ shall occur only in the replacement-list of a function-like macro that uses the ellipsis notation in the parameters.

...

If there is a ... in the identifier-list in the macro definition, then the trailing arguments, including any separating comma preprocessing tokens, are merged to form a single item: the variable arguments. The number of arguments so combined is such that, following merger, the number of arguments is one more than the number of parameters in the macro definition (excluding the ...).

And in the next subsection:

An identifier __VA_ARGS__ that occurs in the replacement list shall be treated as if it were a parameter, and the variable arguments shall form the preprocessing tokens used to replace it.

So you can invoke LOGI or LOGW with as many arguments as you like, and they'll all be expanded at the place specified in the definition by the reference to __VA_ARGS__.