According to the C++ FAQ, macros are evil:
[9.5] Why should I use inline functions instead of plain old #define macros?
Because
#d
unsafe(x)
evaluates the expression x
twice. Once to determine its truth value, and then a second time in one of the two branches of the ternary operator. The inline function safe
receives an evaluated argument: the expression is evaluated once prior to the function call, and the function call works with local variables.
unsafe
is actually not quite as unsafe as it could be. The ternary operator introduces a sequence point between evaluating the test, and evaluating either the consequent or alternative expression.unsafe(x++)
will reliably incrementsx
twice, though, of course, the problem is that this behavior is unexpected. In general, macros which expand an expression more than once do not have this assurance. Usually, they produce outright undefined behavior!
Circa 1999 I produced a library module module for catching uses of macros with side effects.
So, you can write "evil" macros and use them, and the machine will catch situations where they are accidentally used with arguments that have side effects (provided you have adequate code coverage to hit those uses at run-time).
Here is the test program, unsafe.c
. Note that it includes a header file sfx.h
and uses a SFX_CHECK
macro in the expansion token sequence of unsafe
:
#include "sfx.h"
#define unsafe(i) \
( (SFX_CHECK(i)) >= 0 ? (i) : -(i) )
inline
int safe(int i)
{
return i >= 0 ? i : -i;
}
int f(void)
{
return 0;
}
int main(void)
{
int ans;
int x = 0;
ans = unsafe(x++); // Error! x is incremented twice
ans = unsafe(f()); // Danger! f() is called twice
ans = safe(x++); // Correct! x is incremented once
ans = safe(f()); // Correct! f() is called once
}
We compile everything and run from a Linux shell prompt:
$ gcc unsafe.c hash.c except.c sfx.c -o unsafe
$ ./unsafe
unsafe.c:22: expression "x++" has side effects
unsafe.c:23: expression "f()" may have side effects
Note that x++
certainly has side effects, whereas f
may or may not. So the messages are differently worded. A function being called twice is not necessarily an issue, because a function might be pure (have no side effects).
You can get that here if you're curious about how it works. There is a run-time penalty if the debugging is enabled; of course SFX_CHECK
can be disabled so it does nothing (similarly to assert
).
The first time an SFX_CHECK
protected expression is evaluated, it is parsed in order to determine whether it might have side effects. Because this parsing is done without any access to symbol table information (how identifiers are declared), it is ambiguous. The parser pursues multiple parsing strategies. Backtracking is done using an exception-handling library based on setjmp/longjmp
. The parse results are stored in a hash table keyed on the textual expression for faster retrieval on future evaluations.