What Else Could Go Wrong?
JrDebugLogger is a very nice debug logging library. Much of it’s functionality is implemented through macros to allow it to be selectively left out when compiling. Along the way the author has had some interesting problems to solve, and this post is about one of them.
Assume we use the following macro:
to allow us to perform debug logging with a stream-like interface. We can then do:
which expands to:
Now if the compiler knows that debug_on
is false, it can leave out all code
related to the debug logging, since it knows it will never be called. If it
does not know the value at compile time, the resulting code will contain a
very fast check around the call, allowing debug logging to be turned on and
off dynamically with little performance overhead.
There is, however, an insidious bug lurking in the corner, waiting to jump at the user. Can you spot the problem? Think about it for a minute or two before reading on.
Consider this use:
it expands to:
This is valid C++, and compiled without warnings on the three compilers I tried. But who does that else belong to?
Let’s see what the standard says:
An else is associated with the lexically nearest preceding if that is allowed by the syntax.
This is from the C99 standard (6.8.4.1p3), which was the most clear, however statements to the same effect are present in the C++ standards.
So the above is equivalent to:
which was of course not the intention.
So how can we solve this without giving up the nice properties of the if? The simple solution is to give the if in the macro its own else:
We now get the expansion:
and the compiler will correctly associate the users else with his if. So it is equivalent to:
Thanks to Jesse for the nice topic.