P99
Safety of macro programming and pitfalls

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 many 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 much as the for or the ++ operator.

Macros can make your code easier to read and to maintain, more precise and more efficient than would be possible 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
 }

In the example, part of the efficiency also comes from the fact that it is not a function. This is interesting if, for example, some of the variables in the list are of integer type and others are 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, and one important pitfall in particular: you don't see from a macro call if the arguments are evaluated multiple times or not. So if you have the habit of programming with side effects, you really have to be careful.

The simplest solution is avoidance: don't use 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 have 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:

The scripts mentioned above 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.

See also:
P00_DECLARE_ATOMIC_TYPE(0) for argument 1, 2.
P99_AALLOC(T, VB, N) for argument T, VB.
P99_AARG(TYPE, NAME, DIM, VAR) for argument TYPE, DIM.
P99_AASSIGN(TARGET, SOURCE, N) for argument TARGET, SOURCE.
P99_ACALL(ARR, N, TYPE) for arguments ARR, N, TYPE.
P99_ACCESSORS(X, N) for argument X, N.
P99_ACOPY(TYPE, N, ...) for argument __VA_ARG__[0], __VA_ARG__[2].
P99_ALEN(ARR, N) for argument ARR, N.
P99_ALENS(ARR, N) for argument ARR, N.
P99_ANAME(NAME, DIM, VAR) for argument NAME, VAR.
P99_ARE_EQ(FIRST, ...) for argument FIRST.
P99_ARE_ORDERED(OP, ...) for arguments OP, __VA_ARG__[0], __VA_ARG__[1].
P99_ASORT(TAB, ...) for argument TAB.
P99_ASUB(X, T, N, L) for argument T.
P99_ATOMIC_INHERIT(T) for argument T.
P99_AVALUE(X, ...) for arguments X, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_BIGFUNC(FUNC, M, ...) for argument M.
P99_BIGOP(OP, M, ...) for argument M.
P99_CALLOC(T, N) for argument T.
P99_CALL_DEFARG(NAME, M, ...) for argument M.
P99_CALL_DEFARG_LIST(NAME, M, ...) for argument M.
P99_CALL_VA_ARG(NAME, M, T, ...) for argument M.
P99_CASERANGE(START, ...) for argument START, __VA_ARG__[0].
P99_CA_CALL(NAME, ACHECKS, PCHECKS, ...) for arguments ACHECKS, PCHECKS, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_CDIM(NAME, ...) for argument NAME.
P99_CHOOSE5(xT, cc, cs, ci, cl, cll) for argument xT.
P99_CONSTANT(T, NAME, INIT) for argument NAME.
P99_DECLARE_ATOMIC(T, NAME) for argument T, NAME.
P99_DECLARE_ATOMIC_LOCK_FREE(T, NAME) for argument T, NAME.
P99_DECLARE_DELETE(T) for argument T.
P99_DECLARE_ENUM(T, ...) for arguments T, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_DECLARE_ENUM_GETNAME(T, ...) for arguments T, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_DECLARE_ENUM_PARSE(T, ...) for arguments T, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_DECLARE_INIT_ONCE(T, NAME, ARG) for arguments T, NAME, ARG.
P99_DECLARE_INLINE_EXPRESSION(EXT, BASE, EXP, ...) for arguments EXT, BASE, EXP.
P99_DECLARE_INLINE_EXPRESSIONS(NEPL, ...) for argument NEPL.
P99_DECLARE_STRUCT(NAME) for argument NAME.
P99_DECLARE_UNION(NAME) for argument NAME.
P99_DEC_DOUBLE(...) for arguments __VA_ARG__[1], __VA_ARG__[2], __VA_ARG__[4].
P99_DEFINE_ENUM(T) for argument T.
P99_DEFINE_UNION(NAME, ...) for arguments NAME, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_DERIVED_TYPES(T) for argument T.
P99_DESIGNATED(VAR, ...) for arguments VAR, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_DO(TYPE, VAR, ...) for argument TYPE.
P99_ENC_DECLARE(T, NAME) for argument T, NAME.
P99_FCALLOC(T, F, N) for arguments T, F, N.
P99_FHEAD(T, F, P) for argument T.
P99_FIFO_APPEND(L, EL) for argument L, EL.
P99_FIFO_CLEAR(L) for argument L.
P99_FIFO_POP(L) for argument L.
P99_FIFO_TABULATE(TYPE, TAB, L) for arguments TYPE, TAB, L.
P99_FMALLOC(T, F, N) for arguments T, F, N.
P99_FOR(NAME, N, OP, FUNC, ...) for arguments NAME, N, FUNC.
P99_FORALL(NAME, ...) for argument NAME.
P99_FORMAT(...) for argument __VA_ARG__[0], __VA_ARG__[2].
P99_FORMATS(...) for arguments __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_FPRINTF(F, FORMAT, ...) for arguments __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_FREALLOC(P, T, F, N) for arguments T, F, N.
P99_FSIZEOF(T, F, N) for arguments T, F, N.
P99_FUTEX_COMPARE_EXCHANGE(FUTEX, ACT, EXPECTED, DESIRED, WAKEMIN, WAKEMAX) for argument ACT.
P99_GENERIC(...) for argument __VA_ARG__[0], __VA_ARG__[2].
P99_GENERIC_LIT(...) for argument __VA_ARG__[0], __VA_ARG__[2].
P99_GENERIC_SIZE(UI, ...) for arguments UI, __VA_ARG__[1], __VA_ARG__[2].
P99_GENERIC_SIZE_LIT(UI, ...) for arguments UI, __VA_ARG__[1], __VA_ARG__[2].
P99_GEN_ABS(A) for argument A.
P99_GEN_EXPR(BASE, EXPR, ...) for argument BASE, EXPR.
P99_GEN_MAX(A, B) for argument A, B.
P99_GEN_MIN(A, B) for argument A, B.
P99_GEN_SIN(A) for argument A.
P99_GETOPT_DECLARE(CHAR, T, ...) for argument T, __VA_ARG__[0].
P99_GETOPT_DEFINE(CHAR, T, ...) for argument T, __VA_ARG__[0].
P99_GUARDED_BLOCK(T, NAME, INITIAL, BEFORE, AFTER) for arguments T, NAME, AFTER.
P99_HEX_DOUBLE(...) for arguments __VA_ARG__[1], __VA_ARG__[2], __VA_ARG__[4].
P99_HTONL(X) for argument X, 1.
P99_HTONS(X) for argument X, 1.
P99_INITIALIZE(X, L) for argument X, L.
P99_INVARIANT(EXPR) for argument EXPR.
P99_IN_RANGE(R, S, L) for argument R, S.
P99_IPOW(N, X) for argument N, X.
P99_ISSIGNED(T) for argument T.
P99_IS_INF(FIRST, ...) for argument FIRST.
P99_IS_MAX(FIRST, ...) for argument FIRST.
P99_IS_MIN(FIRST, ...) for argument FIRST.
P99_IS_ONE(FIRST, ...) for argument FIRST.
P99_IS_SUP(FIRST, ...) for argument FIRST.
P99_JOIN(...) for arguments __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_LCOPY(TYPE, VAR, ...) for arguments TYPE, VAR, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_LIFO_CLEAR(L) for argument L.
P99_LIFO_POP(L) for argument L.
P99_LIFO_PUSH(L, EL) for argument L, EL.
P99_LIFO_TABULATE(TYPE, TAB, L) for arguments TYPE, TAB, L.
P99_LIFO_TOP(L) for argument L.
P99_LITERAL(...) for arguments __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2], __VA_ARG__[3].
P99_MACRO_PVAR(NAME, ...) for argument NAME, __VA_ARG__[0].
P99_MACRO_VAR(NAME, ...) for argument NAME, __VA_ARG__[0].
P99_MAC_ARGS(...) for arguments __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_MAXOF(0) for argument 1.
P99_MEMSET(TA, SO, N) for argument SO.
P99_MEMZERO(T, TA, N) for argument T.
P99_MINOF(A, B) for argument A, B.
P99_MUTUAL_EXCLUDE(MUT) for argument MUT.
P99_NAME(N, NAME) for argument N.
P99_NTOHL(X) for argument X.
P99_NTOHS(X) for argument X.
P99_OBJLEN(X, ...) for arguments X, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_OBJSIZE(X, ...) for arguments X, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_OVALUE(X, ...) for arguments X, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_PARALLEL_DO(TYPE, VAR, ...) for argument TYPE.
P99_PARALLEL_FORALL(NAME, ...) for argument NAME.
P99_PLAIN_TYPE(T) for argument T.
P99_POINTER_TYPE(T) for argument T.
P99_POSS(N) for argument N.
P99_PRAGMA_DO(PRAG, TYPE, VAR, ...) for argument TYPE.
P99_PRI(xT, F, LEN) for argument xT.
P99_PRINTF(FORMAT, ...) for arguments __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_PROTOTYPE(...) for argument __VA_ARG__[0], __VA_ARG__[2].
P99_PZERO(X, N) for argument X.
P99_QSORT(TAB, NB, ...) for argument TAB.
P99_QVALUE(X) for argument X.
P99_REP(...) for arguments __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_REPEAT(MACRO, N) for argument MACRO, N.
P99_SEP(MACRO, ...) for argument MACRO.
P99_SEQ(MACRO, ...) for argument MACRO.
P99_SER(MACRO, ...) for argument MACRO.
P99_SIGNED_REPRESENTATION(T) for argument T.
P99_SIZE_CHOICE(UI, YES, NO, ...) for arguments UI, YES, NO.
P99_SIZE_INDICATOR(UI, ...) for argument UI.
P99_SNPRINTF(S, N, FORMAT, ...) for arguments __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_SPRINTF(S, FORMAT, ...) for arguments __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_STRDUP(...) for arguments __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_STRUCT_LITERAL(TYPE, ...) for arguments TYPE, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_STRUCT_TYPE0(TYPE, ...) for arguments TYPE, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2], __VA_ARG__[3].
P99_STRUCT_TYPES(TYPE, ...) for arguments TYPE, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2], __VA_ARG__[3].
P99_STRUCT_UNUSE(TYPE, VAR, ...) for arguments TYPE, VAR, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_STRUCT_USE(TYPE, VAR, ...) for arguments TYPE, VAR, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_SVALUE(X) for argument X.
P99_SWAP(_0, _1) for argument _0, _1.
P99_THROW_CALL_RANGE(F, ...) for arguments F, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2], __VA_ARG__[3].
P99_TMAX(T) for argument T.
P99_TMIN(T) for argument T.
P99_TOKJOIN(TOK, ...) for argument TOK.
P99_TO_UNSIGNED(T, MACRO) for argument T, MACRO.
P99_TSS_DECLARE_LOCAL(T, NAME, DTOR) for argument NAME.
P99_TYPE_ARITHMETIC(EXP) for argument EXP.
P99_TYPE_BASIC(EXP) for argument EXP.
P99_TYPE_CHARACTER(EXP) for argument EXP.
P99_TYPE_CHOICE(EXP, YES, NO, ...) for arguments EXP, YES, NO.
P99_TYPE_COMPLEX(EXP) for argument EXP.
P99_TYPE_FLOATING(EXP) for argument EXP.
P99_TYPE_INTEGER(EXP) for argument EXP.
P99_TYPE_REAL(EXP) for argument EXP.
P99_TYPE_REAL_FLOATING(EXP) for argument EXP.
P99_TYPE_SIGNED(EXP) for argument EXP.
P99_TYPE_UNSIGNED(EXP) for argument EXP.
P99_TZERO(X) for argument X.
P99_UINT_DEFAULT(T) for argument T.
P99_UNROLL(MACRO, N) for argument MACRO, N.
P99_UT_MAX(T) for argument T.
P99_VASSIGNS(NAME, ...) for argument NAME.
P99_VECTOR(T, NAME, N) for arguments T, NAME, N.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Defines