Channels ▼

Walter Bright

Dr. Dobb's Bloggers

The X Macro

June 24, 2010

I learned this technique over 30 years ago from friends of mine in college. It was for assembler language macros, but survived the transition to C macros easily. I've used it regularly since, but interestingly have never seen anyone else use it. That makes it my turn to pass along this little gem.

To use a hackneyed example, let's have a header file color.h, and in that there's an enum for the colors:

enum Color { Cred, Cblue, Cgreen };

In the corresponding source file color.c, in order to pretty print colors, there's a corresponding array of strings:

static char *ColorStrings[] =
    {"red", "blue", "green"};

which we can use like:

enum Color c;
...
printf("the color is %s\n",
    ColorStrings[c]);

So far, so good. Time goes on, and another color is added:

enum Color
    { Cred, Cyellow, Cblue, Cgreen };

and yes, we forget to update the ColorStrings[] array, and not only does printing out Cyellow come out as "blue", even worse we have an array overflow printing out the string for Cgreen. (You're a smart programmer and would never make such a mistake, right?)

The trouble is there's no semantic connection between the enum and the array. The usual way to deal with this is to add a pack of unit tests. But wouldn't it be better if we could find a way to connect the dots at compile time? Is this a job for -- The X Macro -- ?

#define COLORS \
    X(Cred, "red") \
    X(Cblue, "blue") \
    X(Cgreen, "green")

Put this in color.h. Then, in the place where the enum is declared:

#define X(a, b) a,
enum Color { COLORS };
#undef X

You can see where this is going. In the source file color.c:

#define X(a, b) b,
static char *ColorStrings[] = { COLORS };
#undef X

In other words, we redefine the X macro as necessary to extract the bit of information needed at the moment and ignore the rest. Proper macro hygiene is maintained because the #define of X will complain if X is already defined, and the #undef will ensure it doesn't muck up any later X.

Adding a new color becomes trivial:

#define COLORS \
    X(Cred, "red") \
    X(Cyellow, "yellow") \
    X(Cblue, "blue") \
    X(Cgreen, "green")

and the enum and array magically get updated automatically. The more experienced programmers will immediately see this can be made more sophisticated:

#define COLORS \
    X(red) \
    X(blue) \
    X(green)

#define X(a) C##a,
enum Color { COLORS };
#undef X

#define X(a) #a,
static char *ColorStrings[] = { COLORS };
#undef X

One real example is in the Digital Mars C++ compiler front end:

#define ENUMSCMAC       \
 X(unde, SCEXP|SCKEP|SCSCT  ) \
 X(auto, SCEXP|SCSS|SCRD    ) \
 X(static, SCEXP|SCKEP|SCSCT) \
 X(thread, SCEXP|SCKEP      ) \
 ...

and 3 separate but parallel constructions are built - the enum, the string table for pretty printing, and the characteristics array (from the second argument). The most complex example I use it for has 6 arguments to the X Macro, which builds enums, struct initializers, and runtime initializers.

Of course, you may already be using a macro or variable named X, and X is hardcoded in the macro body. Andrei Alexandrescu suggests the following improvement where the X macro is itself a parameter:

#define FOR_ALL_COLORS(apply) \
    apply(red) \
    apply(blue) \
    apply(green)

And later:

#define SELECT_STRING(a) #a,
static char *ColorStrings[] =
    { FOR_ALL_COLORS(SELECT_STRING) };
#undef SELECT_STRING 

The X Macro technique works with any language with a half-decent text macro preprocessor, and C's is certainly up to the task. Use it and pass it along, as my friends were kind enough to pass it along to me.

(Like I said, I've never seen this used by others in the last 30 years. Is it really so obscure? Do you already use it? Post a comment!)

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Comments:

ubm_techweb_disqus_sso_-e5b1747a3a22e9e0f93dd5f54c798933
2014-01-07T10:43:52

I have been looking for something like this for some time. Thank you for sharing this and for the clear explanation.


Permalink
ubm_techweb_disqus_sso_-12013654d015384efe1eab46e654cc19
2013-02-03T19:05:26

First of all, Thanks for documenting this!
I saw this technique in some open-source code recently, and was puzzled for a while, then I came across this article, now I know this is a known/good "implementation pattern" :)


Permalink
ubm_techweb_disqus_sso_-345f6dec9c34ef7658efbb506a438114
2012-12-10T09:57:09

Thank you for passing on this little trick!

By the way, I find that for g++ there needs to be a const qualifier for the colorStrings, i.e.

static const char *ColorStrings[] = { COLORS };


Permalink
ubm_techweb_disqus_sso_-567ecde422c950f3e4465f8a140cd8fc
2012-07-26T15:32:59

There is a preview addon for MS Visual Studio to see macro results http://visualstudiogallery.msd.... You can generate final code and insert it unfolded as workaround


Permalink
ubm_techweb_disqus_sso_-8801c7bcb46a51a4ace4952d911a431a
2011-11-08T16:35:04

I've used X-Macros for years too. They are a great tool for a number of things.

Here's a suggestion that makes code a bit easier to read and adds a bit more of a connection between the main source and the X-Macro: place the #undef at the end of the include file.

The only real problem I've had with them is that if you are using them to automatically generate names (which I do frequently), they confuse most IDEs. You don't get some features like auto-completion, and you may end up with false error highlights.


Permalink


Video