If you just want to predicate the execution of a statement/block based on the evaluation of a macro, that dangling else is a risky latent bug that I've seen a lot of teams just live with.
The other solutions presented alter the structure of the code or are otherwise awkward in some fashion. The solution presented below isn't perfect - it has its own awkwardness, but hopefully it is more livable flavor of awkward.
One can generally transform a preprocessor macro's output from the simple but risky form of
if (expression)
to a for loop that will execute 0 or 1 times depending upon expression. As a loop, there's no risk of dangling else. This works like so:
for (bool _internal_once = true; _internal_once && (expression); _internal_once = false)
Note that expression could very well be a complex expression being text substituted, so we need the loop condition to be _internal_once && (expression) to both avoid operator precedence issues, and also guarantee expression is only evaluated once (due to C boolean short circuit logic).
The awkwardness of this solution is that is changes the break and continue behavior within predicated code, made worse because the loop is hidden and it is easy to forget. This has not yet been a problem in real world usage for me. We use these predicate macros to guard metrics, logging, telemetry, and other support code that doesn't often interact with an outer loop.
I have been bit by a real world problem in a previous incarnation of this loop. Never NEVER NEVER use a simple name for the loop control variable, I've seen it shadow another variable and cause the statement/block to see the wrong value.
This solution doesn't force the use of a block/braces on the client's code, but arguably since C and C++ don't, neither should you. It's the client's choice/policy.
One could argue you should generate a more unique name for the loop variable, concatenating ## the identifier with a suffix of __LINE__ or __COUNTER__ (for gnu). That feels excessive to me, the loop control variables are all local/auto and should shadow correctly.
You can even implement predicates like
- DO_ONCE
- Run only once
for (static bool _internal_once = true; _internal_once; _internal_once = false)
- DO_EVERY_N(N)
- Runs once per N iterations
- DO_EVERY_MS(MS)
- Won't run again until at least MS milliseconds has passed
ifthat you don't want to repeat each time.falseso the trace strings are stripped from the executable).