Reduce C- language coding errors with X-macros - Part 2 -

Reduce C- language coding errors with X-macros – Part 2

This article continues the discussion in Part 1 regarding the use of X macros. Here I will examine several more uses of X macros that relieve the programmer from having to manually enforce dependencies and perform data structure sizing calculations.

Counting Entries
Consider  the following use case for populating enumerations and jump tables:

/*  declare an enumeration of state codes */

/* declare a table of function pointers */
p_func_t jumptable[NUM_STATES] = {

There is one thing that I still don’t like about this implementation; the NUM_STATES hack. I call it a hack because although a common technique for defining a count of items, NUM_STATES does not belong inside the enumeration. We could define it as a separate macro, but this requires the developer to remember to update it whenever the enumeration changes.

We can use X macros to place the NUM_STATES macro outside of the enumeration, while at the same time have the pre-processor update it automatically. We start off by declaring a struct as follows:

#define EXPAND_AS_STRUCT(a,b) uint8_t b,
/*  declare a struct with a 1-byte field for every entry */
typedef struct{
} size_struct_t

Now if we apply the size of operator to this struct, the compiler will calculate the number of states, since each state is represented by a one-byte field and the sizeof operator returns the total number of bytes:

#define NUM_STATES      sizeof(size_struct_t)

The concept is that there are valid reasons to define a struct but not instantiate it. We don’t need to create a variable of type size_struct_t for it to be useful.
A little more verbose perhaps than the previous hack, but we succeeded in having the preprocessor automatically generate a NUM_STATES macro without having to pollute the enumeration. A note of caution; this method depends on the compiler not inserting any padding bytes into size_struct_t , so understand your compiler.

Communication Handlers
In my mind this is the “killer app” when it comes to X macro usage. The benefit it provides to the programmer is significant. Here is the general technique:
First create a table containing command names and codes. For our purposes we will ensure that command codes are contiguous:

/*  —— NAME ——- FUNCTION — CODE — */
    #define COMMAND_TABLE
    ENTRY(COMMAND0,    command0,     0x00)
    ENTRY(COMMAND1,    command1,     0x01)
    ENTRY(COMMAND2,    command2,     0x02)
    ENTRY(COMMANDX,    commandX,     0x0X

Both uppercase and lowercase names are in the table, because the upper case names will be used for enumerations while the lowercase will be used for function names.

Next define the command code enumeration:

#define  EXPAND_AS_COMMAND_CODE_ENUM(a,b,c) a##_CMD = c,

If you are not familiar with token concatenation syntax, the ‘## ’ simply appends ‘_CMD ’ onto the end of each enumeration name. In this case the table would be expanded as follows:


Next define structs for each command and response:

typedef  struct {uint8_t command; …}command1_cmd_t;
typedef struct {uint8_t command; …}command2_cmd_t;

typedef struct {uint8_t command; …}command1_resp_t;
typedef struct {uint8_t command; …}command2_resp_t;

Only the command code fields are shown, but presumably there will be other fields defined for each command (length field, optional data fields, crc field etc.).
Now use X macros to define an enumeration of command and response lengths by applying the sizeof operator to the structs. These enumerations will be used in the message handler functions to verify that incoming message lengths are correct and to populate potential length fields in outgoing messages:

        a##_CMD_LEN = sizeof(b##_cmd_t),
        a##_RESP_LEN = sizeof(b##_resp_t),

As previously explained, determine how many commands there are:

#define  EXPAND_AS_STRUCT(a,b,c) uint8_t b,
typedef  struct{
} size_struct_t;

#define NUMBER_OF_COMMANDS sizeof(size_struct_t)

Finally generate the table of function pointers and prototypes. As a reminder, the jumptable will only be correct if the command codes are contiguous.

#define  EXPAND_AS_JUMPTABLE(a,b,c) process_##b,
#define EXPAND_AS_PROTOTYPES(a,b,c) void process_##b(void);
p_func_t jump_table[NUMBER_OF_COMMANDS] = {


With this implementation, when a new command needs to be added the following three steps should be followed.

  1. Add a new row to COMMAND_TABLE .
  2. Define the structure of the command and response messages called commandX_cmd_t and commandX_resp_t .
  3. Write a function to handle the new command called “process_commandX() ”.

The beauty of this is that even though there are three steps, if any are forgotten we will get warnings or errors when attempting to compile. If the structures are not defined, the compile will fail during creation of the enumeration of data lengths. If the command handler function is not written, depending on the compiler it may fail during compilation, but if not it should provide a warning during linking that there is an unresolved external.

One More Thing
Every comms handler is going to have buffers it uses to hold the sent and received messages. The question is, how big should the buffers be? Here is yet another instance where X macros can do the heavy lifting for us.

Since we have defined the format of every command and response, what we want is for the compiler to determine what the largest message is so we can size the buffers appropriately. Here is how you can do that.

#define  EXPAND_AS_BUFFERS(a,c,b) uint8_t b##_buf[sizeof(b##_cmd_t)];

typedef union{

In this case we are using a union, because by definition the size of a union is as large as its largest member. Inside the union we drop via X macros an array of bytes for each command, where the size of the array is the size of the command’s struct . This union is like the size struct – it is not instantiated. Instead we can use the sizeof operator to declare our transmit buffer size.

/*  declare transmit buffer */
uint8_t tx_buf[sizeof(tx_buf_t)];

Now my transmit buffer tx_buf is the optimal size and as I add commands to the comms handler table, my buffer will always be adjusted to the perfect size.

Next Time
In my next article I will discuss how to handle a case where command codes are not contiguous. I will also examine a memory optimization technique where we can use X macros to save valuable memory when jump tables are sparsely populated.

Read Part 1
Read Part 3

Andrew Lucas leads a team of firmware developers at NCR Canada. He is responsible for the firmware architecture of their intelligent deposit modules found inside NCRs line of ATMs.

13 thoughts on “Reduce C- language coding errors with X-macros – Part 2

  1. thanx for the reminding me of some of these clever tricks that the C compiler and pre-processor can perform that can make code maintenance easier. when i code using such useful obfuscation, i usually provide extensive comments to show exactly what the r

    Log in to Reply
  2. I whole heartily agree. In my production code, all of these macros are well documented with detailed comments. Though readability is definitely impacted by this style of coding, the benefits outweigh the weaknesses in my opinion.

    Log in to Reply
  3. That is correct thanks for catching this. It will expand to:

    COMMAND0_CMD = 0x00,
    COMMAND1_CMD = 0x01,
    COMMAND2_CMD = 0x02,

    COMMANDX_CMD = 0x0X,

    Log in to Reply
  4. There is one other bug I noticed.

    #define EXPAND_AS_BUFFERS(a,c,b) uint8_t b##_buf[sizeof(b##_cmd_t)];

    should be

    #define EXPAND_AS_BUFFERS(a,b,c) uint8_t b##_buf[sizeof(b##_cmd_t)];

    Log in to Reply
  5. When I discovered X-Macros, I wished I knew them ice ages back when I had to implement a slave Modbus protocol on a 80186-based RTU. It instantly became clear to me that X-Macros will fit neatly in the error-prone part of mapping Modbus registers to actual

    Log in to Reply
  6. Another small bug my eyes couldn't avoid detecting, the definition of: #define COMMAND_TABLE should be #define COMMAND_TABLE(ENTRY) or course. These X Macros are new to me and I'm so enthusiastic in using them…

    Log in to Reply
  7. Hi guys,
    I have read part – 1 and 2. But, still didn't get the full clarity on this topic. Can any one explain in detailed description for the part -1 and part 2 code. If any real examples how exactly we have to use them?

    I really want to know this topic

    Log in to Reply
  8. “Thank you very much for the helpful article! nnIt seems that part 3 of the article is not correctly linked to nn–Part-3nninst

    Log in to Reply

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.