P99
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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
}
  • This is readable: you have all the variables that are to be checked by the condition at one glance.
  • This is maintainable: just change a variable name in one place, deleted one or add one without problems.
  • This is precise and describes exactly what it does.
  • This is efficient since its replacement is exactly the expression that is needed for that task:
    ((a) < (b)) && ((b) < (c)) && ((c) < (d)) && ((d) < (e)) && ((e) < (f))
    which by itself is not very readable, is difficult to maintain and is not very explicit in its intentions.

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:

  • For the ternary operator, P99 has many cases where the condition is constant and only one branch is evaluated for its type.
  • sizeof is also special because its argument is evaluated mostly for its type and not for its value. There is one exception to that rule, namely variable length arrays, VLA.

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_INTEGRAL_CONSTANT(EXP, TRUE, FALSE) for argument EXP, FALSE.
P99_GENERIC_LIT(...) for argument __VA_ARG__[0], __VA_ARG__[2].
P99_GENERIC_NULLPTR(PEXP, TRUE, FALSE) for arguments PEXP, TRUE, FALSE.
P99_GENERIC_PCONST(PEXP, NCONST, CONST) for argument PEXP.
P99_GENERIC_PCONSTVOLATILE(PEXP, NON, FULL) for argument PEXP.
P99_GENERIC_PQUALIFIED(PEXP, ...) for arguments PEXP, __VA_ARG__[0], __VA_ARG__[1], __VA_ARG__[2].
P99_GENERIC_PVOLATILE(PEXP, NVOLATILE, VOLATILE) for argument PEXP.
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_GENERIC_TCONST(T, NCONST, CONST) for argument T.
P99_GENERIC_TCONSTVOLATILE(T, NON, FULL) for argument T.
P99_GENERIC_TQUALIFIED(T, ...) for argument T.
P99_GENERIC_TVOLATILE(T, NVOLATILE, VOLATILE) for argument T.
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_INTEGRAL_CONSTANT(EXP) for argument EXP.
P99_IS_MAX(FIRST, ...) for argument FIRST.
P99_IS_MIN(FIRST, ...) for argument FIRST.
P99_IS_NULLPTR(PEXP) for argument PEXP.
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_DECLARE(T) for argument T.
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_SEA(MACRO, ...) for argument MACRO.
P99_SEM(MACRO, ...) for argument MACRO.
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_TP_DECLARE(T) for argument T.
P99_TP_REF_DECLARE(T) for argument T.
P99_TP_REF_DEFINE(T) for argument T.
P99_TP_REF_FUNCTIONS(T) for argument T.
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.
f
f
Definition: p99_str.h:138
P99_ARE_ORDERED
#define P99_ARE_ORDERED(OP,...)
Check if the arguments in the list are ordered according to the operation OP.
Definition: p99_for.h:481
d
d
Definition: p99_str.h:138