Running it through the preprocessor shows the problem. Using gcc -E
(can also use cpp -P
, where the -P
option also suppresses generated #
lines),
inline
int safe(int i)
{
return i >= 0 ? i : -i;
}
int f();
void userCode(int x)
{
int ans;
// increment 1 increment 2 (one of these)
// | | |
// V V V
ans = ( (x++) >= 0 ? (x++) : -(x++) );
ans = ( (f()) >= 0 ? (f()) : -(f()) );
ans = safe(x++);
ans = safe(f());
}
As artless noise notes, the function f()
is also called twice by the unsafe
macro. Perhaps it’s pure (has no side-effects) so it’s not wrong, per se. But still suboptimal.
So, since inline functions are generally safer than function-like macros because they work on the same semantic level with the other basic elements: variables and expressions; and for manifest constants, enum
s can often be more tidy; what are the good uses of macros?
Setting constants known only at compile-time. You can define a macro from the command-line when compiling. Instead of
#define X 12
in the source file, you can add
-DX=12
to the cc
command. You can also #undef X
from the command-line with -UX
.
This allows things like conditional-compilation, eg.
#if X
do this;
#else
do that;
#endif
while (loop);
to be controlled by a makefile, itself perhaps generated with a configure script.
X-Macros. The most compelling use for X-Macros, IMO, is associating enum
identifiers with printable strings. While it make look funny at first, it reduces duplication and synchronization issues with these kinds of parallel definitions.
#define NAMES(_) _(Alice) _(Bob) _(Caravaggio) _(DuncanIdaho)
#define BARE(_) _ ,
#define STRG(_) #_ ,
enum { NAMES(BARE) };
char *names[] = { NAMES(STRG) };
Notice that you can pass a macro’s name as an argument to another macro and then call the passed macro by using the argument as if it were itself a macro (because it is one). For more on X-Macros, see this question.