Thursday, July 1, 2010

Preprocessor Questions

1. How can I write a generic macro to swap two values?

A: There is no good answer to this question. If the values are integers, a well-known trick using exclusive-OR could perhaps be used, but it will not work for floating-point values or pointers, (and it will not work if the two values are the same variable, and the "obvious" supercompressed implementation for integral types a^=b^=a^=b is, strictly speaking, illegal due to multiple side-
effects, and...). If the macro is intended to be used on values of arbitrary type (the usual goal), it cannot use a temporary, since it does not know what type of temporary it needs, and standard C
does not provide a typeof operator.

The best all-around solution is probably to forget about using a macro, unless you don't mind passing in the type as a third argument.

2. I have some old code that tries to construct identifiers with a macro like
#define Paste(a, b) a/**/b
but it doesn't work any more.

A: That comments disappeared entirely and could therefore be used for token pasting was an undocumented feature of some early preprocessor implementations, notably Reiser's. ANSI affirms (as did K&R) that comments are replaced with white space. However, since the need for pasting tokens was demonstrated and real, ANSI introduced a well-defined token-pasting operator, ##, which can be used like this:
#define Paste(a, b) a##b


3. What's the best way to write a multi-statement cpp macro?

A: The usual goal is to write a macro that can be invoked as if it were a single function-call statement. This means that the "caller" will be supplying the final semicolon, so the macro body should not. The macro body cannot be a simple brace-delineated compound statement, because syntax errors would result if it were invoked (apparently as a single statement, but with a resultant extra semicolon) as the if branch of an if/else statement with an explicit else clause.

The traditional solution is to use
#define Func() do { \
/* declarations */ \
stmt1; \
stmt2; \
/* ... */ \
} while(0) /* (no trailing ; ) */

When the "caller" appends a semicolon, this expansion becomes a single statement regardless of context. (An optimizing compiler will remove any "dead" tests or branches on the constant condition 0, although lint may complain.)

If all of the statements in the intended macro are simple expressions, with no declarations or loops, another technique is to write a single, parenthesized expression using one or more comma operators. (This technique also allows a value to be "returned.")

4. How can I write a cpp macro which takes a variable number of arguments?

A: One popular trick is to define the macro with a single argument, and call it with a double set of parentheses, which appear to the preprocessor to indicate a single argument:
#define DEBUG(args) {printf("DEBUG: "); printf args;}
if(n != 0) DEBUG(("n is %d\n", n));

The obvious disadvantage is that the caller must always remember to use the extra parentheses. (It is often best to use a bona-fide function, which can take a variable number of arguments in a well-defined way, rather than a macro.

No comments:

Post a Comment