Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are legitimate uses for function-like macros? [closed]

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.

like image 741
Paul Draper Avatar asked Sep 09 '12 18:09

Paul Draper


2 Answers

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.

like image 109
Adrian McCarthy Avatar answered Oct 17 '22 15:10

Adrian McCarthy


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.

like image 2
dmp Avatar answered Oct 17 '22 17:10

dmp