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:
- 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
.