|
P99
|
Often we may hear arguments that function like macros obfuscate the code, are dangerous, not maintainable, in short: the big evil.
I am convinced that this is just one of the other urban legends. Macros as such are not less safe than, say, the use of pointers or of a high quality sharpened kitchen knife. And they belong to C as the for or the ++ operator.
Macros can make your code easier readable, better maintainable, more precise and more efficient than you ever would do by writing code "directly" without macros. It is all about how you write them, if you do that yourself, and how you use them.
As one example look at the following code:
if (P99_ARE_ORDERED(<, a, b, c, d, e, f)) { // all are in order, do something special } else { // handle the base case }
((a) < (b)) && ((b) < (c)) && ((c) < (d)) && ((d) < (e)) && ((e) < (f))
In the example a part of the efficiency also comes from the fact that it is not a function. This is e.g interesting if some of the variables in the list are of integer type and the others of floating type: promotions from integer type to floating type will only be performed where necessary in a comparison of two adjacent variables. In such a way we can profit of the exact comparison for integer types and can avoid the problems of rounding an integer to the next representable double. (Think of comparing e = UINT64_MAX - 1 and e = UINT64_MAX.)
But, macros have pitfalls, in particular one important pitfall: you don't see from a macro call if the arguments are evaluated multiple times or not. So if you have the habit to program with side effects, you really have to be careful.
The simplest really is to avoid that, don't have expressions with side effects such as ++ as arguments to macros or functions. Really, don't do that.
If you are programming macros, you have to be more careful since you can't assume that everybody knows what she or he is doing. For P99 we an automatic suite of scripts that tests if any of the macros that start with "P99_" evaluate their arguments multiple times. This is a bit tricky, special care has to be taken for macros that use the ternary operator ?: and the sizeof operator:
sizeof is special because its argument is also mostly evaluated for its type and not for its value. There is one exception for that rule, variable length arrays, VLA.The mentioned scripts help us detect these and other special cases and the documentation of the corresponding P99 macros is then annotated with warnings and remarks that document the special behavior of these macros.
1, 2. TYPE, DIM. TARGET, SOURCE. ARR, N, TYPE. X, N. __VA_ARG__[0], __VA_ARG__[2]. ARR, N. ARR, N. FIRST. OP, __VA_ARG__[0], __VA_ARG__[1]. TAB. T. M. M. T. M. M. M. START, __VA_ARG__[0]. NAME. xT. T. T, NAME. T. T, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2]. T, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2]. T, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2]. T, NAME, ARG. EXT, BASE, EXP. NEPL. NAME. NAME. __VA_ARG__[1], __VA_ARG__[2], __VA_ARG__[4]. T. NAME, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2]. T. VAR, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2]. TYPE. T, NAME. T, F, N. T. T, F, N. NAME, N, FUNC. NAME. T, F, N. T, F, N. __VA_ARG__[0], __VA_ARG__[2]. __VA_ARG__[0], __VA_ARG__[2]. UI, __VA_ARG__[1], __VA_ARG__[2]. UI, __VA_ARG__[1], __VA_ARG__[2]. A. BASE, EXPR. A, B. A, B. A. T, NAME, AFTER. __VA_ARG__[1], __VA_ARG__[2], __VA_ARG__[4]. X, 1. X, 1. EXPR. N, X. T. FIRST. FIRST. FIRST. FIRST. FIRST. __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2]. TYPE, VAR, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2]. L. L. L, EL. TYPE, TAB, L. L. __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2], __VA_ARG__[3]. NAME, __VA_ARG__[0]. NAME, __VA_ARG__[0]. __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2]. 1. SO. T. A, B. MUT. N. X. X. TYPE. NAME. T. T. N. TYPE. xT. RT, __VA_ARG__[0]. X. TAB. __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2]. MACRO, N. MACRO. MACRO. MACRO. T. UI, YES, NO. UI. __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2]. TYPE, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2]. TYPE, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2], __VA_ARG__[3]. TYPE, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2], __VA_ARG__[3]. TYPE, VAR, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2]. TYPE, VAR, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2]. _0, _1. T. T. TOK. T, MACRO. EXP. EXP. EXP. EXP, YES, NO. EXP. EXP. EXP. EXP. EXP. EXP. EXP. X. T. MACRO, N. T. NAME.
1.7.6.1