Game Boy Advance Dev: Logging to the Console(mattgreer.dev) |
Game Boy Advance Dev: Logging to the Console(mattgreer.dev) |
#ifdef MGBALOG
#define mgbalog(level, format, ...) \
do { _mgbalog(level, format, ##__VA_ARGS__); } while (0)
#else
#define mgbalog(level, format, ...) \
do { } while (0)
#endif
For the exact usage shown here, "mgbalog(...);" will mostly work with the empty replacement-list version, since it just leaves a bare ";". So this is not a guaranteed bug.The reason I prefer the "do { } while (0)" form is that it keeps the macro structurally equivalent to a single statement in both debug and release builds. With the empty disabled macro, this:
if (failed)
mgbalog(ERROR, "failed");
becomes: if (failed)
;
That is valid C, but it can trigger empty-body / suspicious-semicolon warnings, for example GCC’s "-Wempty-body", which matters if the project builds with "-Wextra" or warnings-as-errors.It also means the enabled case is already set up correctly if the macro later grows beyond a single function call. For example, if you later add another statement to the macro:
#define LOG(x) printf("log\n"); printf("%s\n", x)
if (failed)
LOG("failed");
else
recover();
then the expansion breaks because the "else" no longer attaches to the intended "if".So in this specific case the empty macro is probably fine for ordinary "mgbalog(...);" calls, but wrapping both versions in "do { } while (0)" is the more conventional and future-proof C macro shape.