TITLE: drawbacks of macros (Source: comp.lang.c++.moderated, 1 Feb 2001) AUTHOR: "Herb Sutter" > ------------------------------------------------------------------- > Guru of the Week problems and solutions are posted regularly on > news:comp.lang.c++.moderated. For past problems and solutions > see the GotW archive at www.gotw.ca. (c) 2001 H.P.Sutter > News archives may keep copies of this article. > ------------------------------------------------------------------- > > _______________________________________________________ > > GotW #77: #Definition > > Difficulty: 4 / 10 > _______________________________________________________ > > > Common Macro Pitfalls > --------------------- > > >1. Demonstrate how to write a simple max() preprocessor macro that > > takes two arguments and evaluates to the one that is greater, using > > normal < comparison. What are the usual pitfalls in writing such a > > macro? > > There are four major pitfalls, besides several further drawbacks. > Focusing on the pitfalls first, here are common ways to go wrong when > writing a macro. > > > 1. Don't forget to put parentheses around arguments. > > // Example 1(a): Paren pitfall #1: arguments > // > #define max(a,b) a < b ? b : a > > The problem here is that the uses of the parameters a and b are not > fully parenthesized. Macros only do straight textual substitution, so > this can cause some unexpected results. For example: > > max( i += 3, j ) > > expands to the following: > > i += 3 < j ? j : i += 3 > > which, because of operator precedence, actually means: > > i += ((3 < j) ? j : i) += 3 > > This could cause some long debugging sessions. > > > 2. Don't forget to put parentheses around the whole expansion. > > Fixing the first problem, we still fall prey to another subtlety: > > // Example 1(b): Paren pitfall #2: expansion > // > #define max(a,b) (a) < (b) ? (b) : (a) > > The problem now is that the entire expansion is not correctly > parenthesized. For example: > > k = max( i, j ) + 42; > > expands to the following: > > k = (i) < (j) ? (j) : (i) + 42; > > which, because of operator precedence, actually means: > > k = (((i) < (j)) ? (j) : ((i) + 42)); > > If i >= j, k is assigned the value of i+42, as intended. But if i < > j, k is assigned the value of j. > > > 3. Multiple argument evaluation. > > We can fix problem #2 by putting parentheses around the entire macro > expansion, but this leaves us with yet another problem: > > // Example 1(c): Multiple argument evaluation > // > #define max(a,b) ((a) < (b) ? (b) : (a)) > > Now, consider what happens if one or both of the expressions has side > effects: > > max( ++i, j ) > > The result is undefined, because the expanded code tries to modify the > same variable, i, within the same expression (without an intervening > sequence points, which is illegal: > > ((++i) < (j) ? (j) : (++i)) > > Similarly, consider the code: > > max( f(), pi ) > > which expands to: > > ((f()) < (pi) ? (pi) : (f())) > > If the result of f() >= pi, f() gets executed twice, which is almost > certainly inefficient and often actually wrong. > > Alas, although we could work around the first two problems, this one > is a corker -- there is no solution as long as max is a macro. > > > 4. Name tromping. > > Finally, macros don't care about scope. (They don't care about much > of anything; see GotW #63.) They just perform textual substitution no > matter where the text may be. This means that, if we use macros at > all, we have to be very careful about what we name them. In > particular, the biggest problem with the max macro is that it is > highly likely to interfere with the standard max() function template: > > // Example 1(d): Name tromping > // > #define max(a,b) ((a) < (b) ? (b) : (a)) > > #include // oops! > > The problem is that, inside header , there will be > something like the following: > > template const T& > max(const T& a, const T& b); > > which the macro "helpfully" turns into an uncompilable mess: > > template const T& > ((const T& a) < (const T& b) ? (const T& b) : (const T& a)); > > If you think that's easy to avoid by putting your macro definition > after all #included header files (which really is a good idea in any > case), just imagine what the macro does to all your other code that > happens to have variables or other things that just happen to be named > max. > > If you have to write a macro, try to give it an unusual and > hard-to-spell name that will be less likely to tromp on other names. > > > Other Macro Drawbacks > --------------------- > > There are a few other major things a macro can't do: > > > 5. Macros can't recurse. > > We can write a recursive function, but it's impossible to write a > recursive macro. As the C++ standard says, in 16.3.4/2: > > If the name of the macro being replaced is found during this scan of > the replacement list (not including the rest of the source file's > pre- processing tokens), it is not replaced. Further, if any > nested replacements encounter the name of the macro being replaced, > it is not replaced. These nonreplaced macro name preprocessing > tokens are no longer available for further replacement even if they > are later (re)examined in contexts in which that macro name > preprocessing token would otherwise have been replaced. > > > 6. Macros don't have addresses. > > It's possible to form a pointer to any free or member function (for > example, to use it as a predicate), but it's not possible to form a > pointer to a macro because a macro has no address. Why not should be > obvious: Macros aren't code. A macro doesn't have any existence of > its own, because all it is is a glorified (and not particularly > glorious) text substitution rule. > > > 7. Macros are debugger-unfriendly. > > Besides the fact that macros change the underlying code before the > compiler gets a chance to see it, and therefore can wreak havoc with > variable and other names, a macro can't be stepped into during > debugging. > > Have you heard the one about the scientists who started experimenting > on lawyers instead of on laboratory macros? It was because... > > > There Are Some Things Even a Macro Just Won't Do > ------------------------------------------------ > > There are valid reasons to use macros (see GotW #32), but there are > limits. This brings us to the final question: > > >2. What can a preprocessor macro not create? Why not? > > In the standard, clause 2.1 defines the phases of translation. > Preprocessing directives and macro expansions take place in phase 4. > Thus, on a compliant compiler, it is not possible for a macro to > create: > > - a trigraph (trigraphs are replaced in phase 1); > > - a universal character name (\uXXX, replaced in phase 1); > > - an end-of-line line-splicing backslash (replaced in phase 2); > > - a comment (replaced in phase 3); > > - another macro; or > > - changes to a character literal or string literal via macro names > inside the strings. For this last point, as noted in 16.3/8 > footnote 7: > > Since, by macro-replacement time, all character literals and string > literals are preprocessing tokens, not sequences possibly containing > identifier-like subsequences (see 2.1.1.2, translation phases), they > are never scanned for macro names or parameters. > > A recent CUJ article[1] claimed that it's possible for a macro to > create a comment: > > #define COMMENT SLASH(/) > #define SLASH(s) /##s > > This is nonstandard and not portable, but it actually works on some > compilers. Why? Because those compilers don't implement the phases > of translation exactly correctly. Here are the results from four > compilers I tried: > > Microsoft DevStudio.Net (Visual C++ version 7), beta 1: Accepts > the code (wrong) > > Borland BCC 5.5.1: Accepts the code (wrong) > > GCC 2.95.2: Rejects the code (correct) > > Comeau 4.2.44: Rejects the code (correct) > > > > Notes > ----- > > 1. M. Timperley. "A C/C++ Comment Macro" (C/C++ Users Journal, 19(1), > January 2001). > > > --- > Herb Sutter (mailto:hsutter@acm.org) > > Contributing Editor, C/C++ Users Journal (http://www.cuj.com) _______________________________________________ cpptips mailing list http://cpptips.hyperformix.com