Macros are not evil. Some people who love C++ too much will try and tell you that you should never use a macro. That is just wrong, macros when used correctly can do many things that inline functions can't (yet) do and can result in code that is easier to write and (more importantly) easier to read. For example try re-writing the following in an equally efficient fashion without macros:
int write(char *buf, int len); // prototype for a function to write some data
#define WRITESTR(XXbuf) write((XXbuf), sizeof(XXbuf))

struct FOO
{
 char item1[4];
 char item2[8];
};

int main()
{
 FOO object;

 WRITESTR(object.item1);
 WRITESTR(object.item2);
}
As you can see if I change the size of item1 or item2 I don't have to go through my code and change buffer offsets if I have to change the size of objects in the struct. I used code very similar in concept to this a couple of years ago on a bank project. The code wasn't as simple as this, but the project wasn't easy either. The result was that the (almost daily) changes to the data structures returned from the mainframe were quickly incorporated into my code. My aim was that when one of the mainframe people informed me of a change in the sizes of the structure elements I would have the change coded and compiled before he had walked back to his desk (I usually achieved that aim).
This is only one example of a good use of a macro. I could provide a dozen other ways that macros can be used to write code that is easier to read, understand, and maintain.
Now here's what you shouldn't do with macros:
  1. Firstly if you have a macro that acts like a function (IE it has code in it as apposed to the "#define abc 1" type of macro that gets the most use) then call it in a way that looks like a function. Call it with parenthesis after it's name (even if it takes no parameters) and then put a semi-colon after it. This will avoid confusion for the next programmer who works on your code, and if you use an automatic source reformatter such as ALT-F8 in VC it won't get confused.
  2. Don't ever have a macro that has a "return" statement. There's nothing more annoying when you're trying to read the code of an unskilled programmer and you have to do a search on the contents of every second line of code to determine if it's a macro, and if so whether it does a return. If a line of code is setting variables or doing data IO I can generally get the idea of what the code is doing without reading the source of the function, with a well written macro it's the same. If the macro may be doing a return then the code is much harder to read and productivity goes down.
  3. Macros should never use local variables in your function unless they are defined locally - even then it's usually a bad idea. If they allocate their own variables (something you don't want to do if you can avoid it) then they should start with XX or some other code that makes a name clash with macro parameters very unlikely. There are situations (such as code involving varargs) where a macro defined in the local source file which uses variables in the function can save a lot of duplicated code and save the programmer time. That's OK. But having source in a dozen modules having variables with particular names for macros to use makes for code that's difficult to maintain.
  4. Put parenthesis around all macro parameters. If the macro expands to a numeric expression then also put parenthesis around the macro itself. See the following:
  5. #define MACRO(XX) XX*2
    a = MACRO(b + 1); // expands to "a = b + 1 * 2" not "a = (b + 1) * 2" as we want
    #define MACRO2(XX) XX +1
    c = MACRO2(d) * 2; // expands to "c = d + 1 * 2" not "c = (d + 1) * 2"
    Here's the correct way to define those 2 macros and avoid nasty surprises:
    #define MACRO(XX) ( (XX)*2 )
    #define MACRO2(XX) ( (XX)+1 )
  6. When defining a macro that expands to multiple statements then it's good to do something like the following example from the include/linux/sched.h in the source to the Linux kernel 2.1.99:
  7. #define SET_LINKS(p) do { \
            (p)->next_task = &init_task; \
            (p)->prev_task = init_task.prev_task; \
            init_task.prev_task->next_task = (p); \
            init_task.prev_task = (p); \
            (p)->p_ysptr = NULL; \
            if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) \
                    (p)->p_osptr->p_ysptr = p; \
            (p)->p_pptr->p_cptr = p; \
            } while (0)
    That code allows the caller of the macro to write this sort of code:
    if(a)
     SET_LINKS(a);
    else
     SET_LINKS(B);
    So the macro can be used exactly as a function would be. If you do what most programmers do and just define the macro to expand to a list of statements then it'll totally fail in the above example. However you can just use blocks in all if conditions to ensure that the program will still work even if a single line expands out to many statements, and I wouldn't criticise someone for coding in such a fashion.

There was a bug in this web page (IE a sample of C code which would not compile). Thanks to Robert Wall for noticing this and reporting it to me.
Copyright © 2000 Russell Coker, may be distributed freely.