There is no shortage of examples of bad or dangerous function-like macros in C/C++.
#define SQUARE(x) x * x
printf("%d", SQUARE(4)); //16
printf("%d", SQUARE(3+1)); //7
#undef SQUARE
#define SQUARE(x) (x) * (x)
printf("%d", 36/4); //9
printf("%d", SQUARE(6)/SQUARE(2)); //36
#undef SQUARE
#define SQUARE(x) ((x) * (x))
int x = 3;
++x;
printf("%d", SQUARE(x)); //16
int y = 3;
printf("%d", SQUARE(++y)); //?
#undef SQUARE
Given the problems with function-like macros, what examples are there of good/prudent/recommended uses for them?
Are there any times when a function-like macro would be preferrable to a function?
I imagine there must be some really good cases of this, or else the preprocessor makers would have made their job easier and left them out.
The comments have captured most of it.
Some types of debugging and instrumentation can be accomplished only by use of a function-style macro. The best example is assert()
, but function-style macros can also be used for instrumenting code for profiling as well. Magic macros like __FILE__
and __LINE__
as well as features like #
for quoting and ##
for token-pasting make function-like macros valuable for the debugging and profiling.
In C++, with templates and typically more aggressive inlining, there are few, if any, other reasons to use a function-style macro. For example, the template function std::max
is a much better solution than a MAX
macro for the reasons illustrated in the question.
In C, it's sometimes necessary in optimization to ensure the a small piece of code is inlined. Macros, with all their caveats, are still occasionally useful in this context. The ALLCAPS naming convention is there to warn programmers that this is actually a macro and not a function because of all the problems with simple text substitution. For example, if you had several places where you needed the equivalent of std::max
in a performance-critical piece of code, a macro--with all its dangers--can be a useful solution.
Most of the time you don't need to use macros. However there are some cases that are legitimate (although there is room for discussion).
You should not use macros when you can use enums. You should not have macros that depend on having a local variable with a magic name. You should not have macros that can be used as l-values. You should not have macros that have side effects on the code around the expanded macro. Using macros instead of inline functions is most of the time a bad idea, in any case the list is endless.
You could use a macro to fake an iterator though, in C and in particular the Linux Kernel you will see the following:
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
There are numerous other similar type of macros that are used throughout the Linux kernel source.
Also offsetof(3) is typically implemented as a macro, as well as assert(3) etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With