Writing C/C++ Macros: Rules, Tricks and Hints
by Stanislav Sýkora, Extra Byte, Via R.Sanzio 22C, Castano Primo, Italy 20022
in Stan's Library, Ed.S.Sykora, Vol.I. First release April 15, 2004.
Permalink via DOI:  10.3247/SL2Soft07.004
Other programming articles in Stan's Library | Math books | Software books and Links SCIENCE Links | Stan's HUB

Though most programmers would not classify writing macros as an art, there are a few things which one should know and which are rarely explained in detail in language-reference books. Novices mostly find out the subtle dangers of macro programming by trial and error which is definitely NOT the most efficient approach.

This note attempts to list the most often encountered problems and help beginners to avoid committing time-consuming mistakes. I plan to expand it for some time so, if the argument interests you, come back and check occasionally what has been added.

For examples of macros, see the Code Snippets section of Stan's Library.

Updates: March 22, 2007 (Section 9) and May 25, 2008 (expression macros with side-effect assignments).



1. What is a macro

Let us start with a somewhat simple-minded example of a macro (not to be emulated in any real project):

#define SquareOf(x) x*x

It defines a kind of function which, used in an actual piece of code, looks exactly like any other function call:

double yout,xin=3;
yout = SquareOf(xin);

As we shall see in a moment, the problem is that SquareOf only pretends to be a function call, while it is really something completely different.

The formal syntax of a macro is:

#define name(dummy1[,dummy2][,...]) tokenstring

The symbols dummy1, dummy2, ... are called dummy arguments (as usual, square brackets indicate optional items). There are a few additional rules such as that the macro can extend over several lines, provided one uses a backslash to indicate line continuation:

#define ThirdPowerOf(dummy_argument) \
         dummy_argument \
        *dummy_argument \
        *dummy_argument

You are of course expected to break the line at a reasonable position and not, for example, in the middle of a symbol. These aspects, though not trivial when writing a compiler, are quite intuitive and do not cause many problems in practice.

2. How does a compiler handle a macro

What makes a macro different from a standard function is primarily the fact that a macro is a scripted directive for the compiler (rather than a scripted piece of run-time code) and therefore it gets handled and done with at compilation time rather than at run time.

When the compiler encounters a previously defined macro, it first isolates its actual arguments, handling them as plain text strings separated by commas. It then parses the tokenstring, isolates all occurrences of each dummy-argument symbol and replaces it by the actual argument string. The whole process consists entirely of mechanical string substitutions with almost no semantic testing!

The compiler then substitutes the modified tokenstring for the original macro call and compiles the resulting code script. It is only in that phase that compilation errors can occur. When they do, the result is often either amusing or frustrating, depending upon how you feel at the moment. You are getting mysteriously looking error messages resulting from the modified text and thus referring to something you have never written!

3. Why should that be a problem

The answer to this question is evident from the results of the following small console program which is formally correct and compiles without any problem:

#include <stdio.h>
#define SquareOf(x) x*x
void main() {
  int xin=3;
  printf("\nxin=%i",xin);
  printf("\nSquareOf(xin)=%i",SquareOf(xin));
  printf("\nSquareOf(xin+4)=%i",SquareOf(xin+4));
  printf("\nSquareOf(xin+xin)=%i",SquareOf(xin+xin));
}

Intuitively, you probably expect the output of this program to be:

xin=3
SquareOf(xin)=9
SquareOf(xin+4)=49
SquareOf(xin+xin)=36

What you actually get, however, is this:

xin=3
SquareOf(xin)=9
SquareOf(xin+4)=19
SquareOf(xin+xin)=15

Before you conclude that computers are stupid and become a farmer, consider what has happened. When the compiler met the string "SquareOf(xin+4)", it replaced it with the string "x*x" and then replaced each of the dummy-argument-strings "x" by the actual-argument-string "xin+4", obtaining the final string "xin+4*xin+4" which, in fact, evaluates to 19 and not to the expected 49. Likewise, it is now easy to work out the case of SquareOf(xin+xin) and understand why and how the result differs from the expected one.

The problem would have never happened if SquareOf(x) were a normal function. In that case, in fact, the argument xin+4 would be first evaluated as a self-standing expression and only then would the result be passed to the function SquareOf for the evaluation of the square.

Actually, none of the two ways is wrong. They are just two different recipes on how to handle the respective scripts. Given the formal similarity between the macro function call to a standard function call, however, the discrepancy is dangerous and should be removed. Fortunately, there is a simple way to do so. Replacing the original definition of the SquareOf macro by

#define SquareOf(x) (x)*(x)

the problem vanishes because, for example, the macro-call string "SquareOf(xin+4)" is transformed into "(x)*(x)" and then into "(xin+4)*(xin+4)" which evaluates exactly as intended.

4. Other potential dangers

Since a macro-call leads to simple string replacements prior to actual compilation, the compiler can not immediately interpret the script in semantic terms and thus does nothing about things like precedence of operators, type compatibility, assignment rules, referencing/de-referencing rules, etc. The result is a whole series of dangers, some of which less obvious than the one we have seen in the preceding Section. Yet being less obvious does not make them less serious, especially since a faulty macro may work fine in dozens of test examples and then fail when the survival of a billion-dollar project depends on it.

One reason why faulty macros can be a real nuisance is the fact that compilers ignore macros until they are invoked. This is sometimes handy. For example, if macro A(...) contains a call to macro B(...) there is no reason for the definitions of the two macros to appear in any particular order. If the definitions are in a header file, that of macro A may precede the one of macro B. It is only important that both macros be defined when macro A is actually called. On the other hand, when a macro is never invoked, its definition is completely irrelevant. Your header file may therefore contain faulty or even nonsensical definitions of a number of unused macros and you will not find out until, at some later revision, you actually invoke one of them!

As an example of the possible misunderstandings due to the lack of operator precedence checks, consider the following code and its output:

#include <stdio.h>
#define SumOf(x,y) (x)+(y)
void main() {
  int arg1=1, arg2=7;
  printf("\narg1=%i,arg2=%i",arg1,arg2);
  printf("\nSumOf(arg1,arg2)=%i",SumOf(arg1,arg2));
  printf("\n2*SumOf(arg1,arg2)=%i",2*SumOf(arg1,arg2));
}

Output:

arg1=1,arg2=7
SumOf(arg1,arg2)=8
2*SumOf(arg1,arg2)=9

Clearly, the second result is not what we would expect if SumOf were a normal function. This is because, according to the macro handling rules, the macro call script

"2*SumOf(arg1,arg2)"

is transformed into

"2*(arg1)+(arg2)" and not "2*((arg1)+(arg2))".

To prevent the discrepancy from happening, the simplest way is to enclose the whole tokenstring into parentheses to make sure that it is always treated as an unbreakable, complete expression. The following examples show the 'correct' versions of the two macros we have discussed so far (it is quite safe to use these ones in a real project):

#define mSquareOf(x) ((x)*(x))
#define mSumOf(x,y)  ((x)+(y))

The outermost parenthesis are not necessary when the tokenstring is simply a call of another function or when it is not a value-returning expression, such as in these examples:

#define mMyMacro(x) mMyOtherMacroOrFunction((x)*(x)+2,10)
#define LimitFromAbove(x,limit) if ((x)>(limit)) (x)=(limit)

The second of these macros is clearly intended as a full statement and not as a value-returning function. It could be used as an in-line equivalent of the following regular function:

void LimitFromAbove(&x,limit) {if (x>limit) x=limit;}

When LimitFromAbove is actually invoked, such as in the following statement, there is no way to tell whether it is a macro or a regular function:

...
LimitFromAbove(array[2*i+1],1000);
...

This, on one hand, is desirable because the formal equivalence in using functions reduces the probability to committing errors. It is one of the reasons why the terminating semicolon is virtually never included in the macro (though it could be). It is up to the user of the macro to place it after the actual call statement exactly as in the case of any other C/C++ statement.

On the other hand, it is a good thing to be able to tell at first sight that a piece of code involves a macro. In particular, when a syntax error occurs, the compiler messages for the macro call may be quite different from those for a normal function call and it helps to be reminded of the fact that the mischievous code contains a macro.

The easiest way to reconcile the two requirements is by using a private convention which makes the names of macro functions distinct from those of all other entities, including standard functions. I do it by starting such names with a small m. For example, I would use the name mSquareOff for a macro and reserve SquareOf for a standard-function implementation of the argument-squaring procedure.

5. Do all macros return a value?

They don't. Some macros are expressions and behave like value-returning functions, others are actions and can be viewed as analogs of complete statements or, alternatively, functions returning void.
Examples of the two categories are the macros

#define mSquareOf(x) ((x)*(x))
#define mLimitFromAbove(lv,limit) {if ((lv)>(limit)) (lv)=(limit);}

We have already seen that whenever a macro can be used as an expression, it is best to enclose its tokenstring in parentheses. In the case of macros which do not return a value, they usually translate into a full C/C++ statement. In such a case, it is best to actually terminate the statement by a semicolon and then enclose the whole tokenstring into curly brackets, the way we have just done in the case of mLimitFromAbove. When the macro is invoked in a standard way, like in the following code, the translated script contains an extra semicolon, amounting to a void statement:

...
mLimitFromAbove(array[i],1000);
...
/* translates to */
...
{if ((array[i])>(1000)) (array[i])=(1000);};
...

Since void statements are legal and don't generate any executable code, this is a little price to pay for the improved readability of the macro code. The curly brackets make explicit the distinction between the two types of macros and, above all, make it much clearer what we have in mind. The convention also fits nicely with the conventions for macros containing more than one statement (Section 8).

There is one aspect of the curly brackets convention which some people might view as a limitation. Consider the following code which is formally correct:

#define mHackersLimitFromAboveEx(x,limit) if ((x)>(limit)) (x)=(limit)
...
mHackersLimitFromAboveEx(array[i],1000)+n;
...
/* which translates to */
...
if ((array[i])>(1000)) (array[i])=(1000)+n;
...

There are people who might consider this way of setting x to a 'limit plus an extra' whenever x exceeds the limit as a versatile enhancement of the macro. Such thinking, however, is typical of a confused hacker's mentality and should be vigorously opposed. The proper way of doing the same thing is by means of a dedicated macro such as:

#define mLimitFromAboveEx(lv,limit,extra) {if ((lv)>(limit)) {(lv)=(limit)+(extra);}

Occasionally, one runs into macros which just generate composite pieces of source code and do not belong to any of the two categories discussed above. Such macros are rather rare and, if possible, should be avoided. In any case, they must be left exactly as they are; adding any kind of parenthesis or brackets would interfere with their functionality.

6. Use of pointers in macros

Consider again the macro mSquareOf(x). When it is invoked, its dummy argument x is replaced by an actual argument which can be either an expression or a simple variable. In the latter case, we might prefer to use a pointer to the variable rather than using its value. The macro would then have to be written as

#define mpSquareOf(pX) ((*(pX))*(*(pX)))
...
/* and invoked as (an example) */
...
for (i=0;i<N;i++) sum2 += mpSquareOf(array+i);
...
/* rather than */
...
for (i=0;i<N;i++) sum2 += mSquareOf(array[i]);
...

This, of course, looks like an unwarranted complication and, in such an oversimplified case, that's what it really is. In general, when there is no compelling reason for using pointers rather than values, values are more universal since they accept composite expressions. For example, one can invoke mSquareOf(2*y+1) but not mpSquareOf(&(2y+1)) because addresses of expressions are undefined. At the same time, if pX is pointer to a variable x, the invocation mSquareOf(*pX) is perfectly legal and equivalent to mpSquareOf(pX). The difference lies in this case only in the place where we do the de-referencing - whether in the macro or in its actual invocation.

There are, however, situations where the use of pointers is preferable or even unavoidable. Before discussing some of them, let us take another look at two formal aspects of the above example:
  a) In the name of the dummy argument, we have used the prefix letter p to stress the fact that it must be a pointer. It is a good idea to do this systematically.
  b) In the tokenstring, each occurrence of the dummy argument is again enclosed in parentheses, even though it is a pointer. This is essential since actual pointer values may be the results of pointer-evaluation expressions. For example, applying the de-referencing operation *(pX) to the actual argument array+i yields *(array+i) which is what one expects. Without the parentheses, the result would be *array+i which is something completely different!

The second-level parentheses in the tokenstring of mpSquareOf were added simply for better readability. The expressions ((*(pX))*(*(pX))) and (*(pX)**(pX)) are in fact equivalent but the latter one looks a bit messy (in addition, it relies implicitely on the operator-precedence conventions of the C language).

One situation where the use of pointers is unavoidable is when an entity of a certain type is to be interpreted as being of another type. For example, assume that I wonder how my system encodes floating-point numbers of the type float. We know that it uses 4 bytes (we can use sizeof(float) to find out) so we would like to write a macro which extracts the k-th byte out of a variable of the type float. The easiest way to do so (short of resorting to an ad-hoc defined union) is this:

typedef unsigned char BYTE;
#define mByteOf(k,pEntity) (((BYTE*)(pEntity))[k])
...
/* examples of invocation */
float f;
MyMegaStructure* Mms;
BYTE f_byte_2 = mByteOf(2,&f);
BYTE Mms_byte_33 = mByteOf(33,Mms);
...

Notice that the data extraction macro mByteOf can be used to extract the k-th byte of any pointed-to multi-byte data entity. It first converts the 'pointer to entity' to a BYTE* (casting between pointers is always legal) and then extracts the k-th byte of the byte array. Clearly, in this case the use of pointers is almost mandatory. Notice also that casting between pointers does not modify the bits-content of any expression, while the same is not true in type-conversion casting such as (BYTE)(f) which implies more or less complicated arithmetics and, in addition, need not be defined in the case of user-defined types.

7. Assignments and left-value entities in macros

We have seen in Section 5 that some macros stand for complete C/C++ statements. Since the most common statement type is an assignment statement, we should spend a little time on analyzing the specific aspects of macros which translate into a statement containing the assignment operator =.

An example of such a macro was mLimitFromAbove(lv,limit) in which an assignment is executed under the condition that lv exceeds the limit. A more straightforward example is:

#define mNewtonIter(lvSqrApprox,x) \
   {(lvSqrApprox)=((lvSqrApprox)+(x)/(lvSqrApprox))/2;}

One thing the reader has certainly noticed in both macros is the use of the prefix lv for the dummy argument to which the macro assigns a new value. It stands for left-value, a C/C++ slang for entities which have a well-defined storage location and can accept values.

A plain variable like AgeOfUniversen is a left-value, but an expression like (2*AgeOfUniverse) is not. In principle, a left-value is anything that could appear on the left-hand side of an = operator. It includes also array elements (such as arr[3][2*i+1], structure and class members (such as myclass.mystruct.array1[n+2]), but not temporary-value expressions.

It is therefore a good idea to adopt a distinctive mark such as the lv prefix for those dummy arguments which must be left-values. It is a reminder for the user of the macro that there is a restriction imposed on the actual argument during invocation and it may also help you in debugging should you receive a compiler message of the type "illegal left-value".

In C and C++, an assignment is also interpreted as a value-returning expression. Consequently an expression macro enclosed in simple parentheses like this one

#define mToggleBit0(lvx) ((lvx)^=1)

can be used either as an assignment statement, or as an expression with a side-effect assignment. While such side effects are legal, use them parsimoniously. A complex expression with many side-effect assignments can be quite unpredictable because the order in which the assignments are made might depend upon the particular compiler you are using. A single side-effect assignment is OK, but are you sure what might be the result of an expression like ((y += 3)*(x*=x+y)*(y = 3*y+x))?

8. Use of composite statements in macros

There is no reason why action macros should not contain whole blocks of statements.
The following example clarifies the concept:

#define mSwap(lvA,lvB,lvAux) {(lvAux)=(lvB);(lvB)=(lvA);(lvA)=(lvAux);}
...
/* example of use */
double x,y,aux;
...
if (x<y) mSwap(x,y,aux);
...

This particular macro swaps (exchanges) the values of the two entities lvA and lvB. In order to do so, it needs an auxiliary entity lvAux as a temporary storage and it has to combine three statements.

Ideally all three dummy arguments should be of the same type (unless you can tolerate funny side-effects). The type itself, however, is essentially irrelevant (that is the reason for using the term entity). For example, it could be an int, unsigned char, double or, for that matter, any user-defined type (structure or class) as long as it is compatible with the assignment operator.

The above example of mSwap usage illustrate also why the whole block of statements contained in the tokenstring must be enclosed in curly brackets. With the brackets, the example in fact translates to

if (x<y) {aux=y;y=x;x=aux);

which is what we had in mind, while without them we would obtain

if (x<y) aux=y;y=x;x=aux;;

which is completely wrong (though semantically correct) since the conditional execution regards only the first of the three statements.

9. Local variables in composite-statement macros

With composite-statement action macros we may occasionally encounter the need for a variable which is only used locally. This is possible, thanks to the fact that the scope of a variable declared within a block does not extend beyond the block itself. One needs to be careful a keep in mind that, unlike a true function, a macro is just a text replacement. The local variable, whatever its name, hides any variable of the same name declared outside (before) the macro. It is therefore a good practice to adopt distinctive syntax for local variables in action macros (for example, you might choose a prefix like _m_name).

Let us now consider an example which clarifies several aspects of action macros, including the use of local variables. The macro, mBitSum(n,lvResult) counts non-zero binary bits in an integer expression n and places the result into the left-value lvResult. It uses an efficient algorithm based on the fact that when n!=0 then n&(n-1) differs from n only by having the lowermost non-zero binary bit reset to 0 (for example, when n is binary 00101100, then n&(n-1) is 00101000). Here is the code:

#define mBitSum(n,lvResult) \
    {unsigned long _m=(n);(lvResult)=0;while (_m) {_m&=_m-1;(lvResult)++;}}

The points to be noticed about this are:
- The local variable _m is first set equal to n (remember that n may be a complex expression). It hides any variable named _m which might be defined at the moment when the macro is called. This is not a problem, though, since, even though the macro zeroes _m, it affects only its own local _m, not the one with the broader scope.
- Since the arguments n and lvResult might be expression, it is good pratice to put them anyway in parentheses in the tokenstring, even though it might seem excessive in this case (you never know what people will pass to a macro).
- There is no need to enclose in parentheses the local variable (it will never get replaced by anything more complex).
- Unlike a true function, the action macro can not return a value and thus can not be part of an expression, such as 3+7*mBitSum(n,lvResult). All it can do is to set the result into a left-value destination which, afterwards, can be used in whatever desired way.

APPENDIX: The decalog of a prudent macro writer

 1) Use a characteristic naming convention allowing you to distinguish at first sight all your macro function names.
In this Note, for example, the Author advocates starting the names of all C/C++ macros with a small m.

 2) Before writing a macro, make up your mind whether it should be an expression macro (one which evaluates to a numeric or boolean value and can be used as a component part of larger algebraic or relational expressions) or an action macro (one which corresponds to one or more C/C++ statements or to function returning void). If possible, avoid mixing the two types and/or writing macros which are neither expressions, nor sequences of complete C/C++ statements.

 3) When a dummy argument stands for a value or for a pointer to a value,
enclose all its occurrences in the tokenstring in parentheses.

 4) Enclose the entire tokenstring of expression macros in parentheses.

 5) In action macros, terminate each statement with a semicolon and enclose the whole tokenstring in curly brackets.

 6) When a dummy argument of a macro function must be a pointer, make it evident from its name
(for example, by means of the prefix p).

 7) When a dummy argument of a macro function must be an L-value (i.e., an entity which can be assigned a value),
make it evident from its name (for example, by means of the prefix v).

 8) Avoid macros with complicated and possibly unpredictable side effects.
Ideally, the name of a macro should say what the macro does and it should do just that, not more.

 9) Collect all your macros in header files and accompany the declaration of each macro with a brief explanation of its function. Manuals and other documentation get lost while headers, being essential for the compilation, don't.

10) Beware of hacker's tricks of the type 'you would never believe this works'. There are two reasons for the diffidence:
      a) Most likely, the next compiler will not believe it, too!
      b) In a surprisingly short time, you will not be able to decipher what you yourself had in mind.


TOP | Other programming articles in Stan's Library | Math books | Software books and Links SCIENCE Links | Stan's HUB
Copyright ©2004,2007 Stanislav Sýkora    DOI: 10.3247/SL2Soft07.004 Designed by Stan Sýkora