C X macros are a metaprogramming technique where a macro is defined to represent a list, then included multiple times with different expansions to generate repetitive code. The same list definition generates enums, string arrays, case statements, and parser tables without manual duplication.
Use X macros to keep related data (enums, strings, handlers) synchronized and avoid repetition when the same list is used in multiple contexts.
This example shows X macros generating enums, strings, and case statements from one list.
// compile: gcc -o xmacros xmacros.c // run: ./xmacros // description: X macro pattern for code generation #include <stdio.h> #include <string.h> // Define the list once (the "X" macro pattern) #define COLORS_LIST \ X(RED, "Red", 0xFF0000) \ X(GREEN, "Green", 0x00FF00) \ X(BLUE, "Blue", 0x0000FF) \ X(YELLOW, "Yellow", 0xFFFF00) \ X(CYAN, "Cyan", 0x00FFFF) // Generate enum #define X(name, str, hex) name, enum Color { COLORS_LIST COLOR_COUNT }; #undef X // Generate string array #define X(name, str, hex) str, const char *color_names[] = { COLORS_LIST }; #undef X // Generate RGB array #define X(name, str, hex) hex, const int color_values[] = { COLORS_LIST }; #undef X // Function to get color name const char *color_name(enum Color c) { if (c >= 0 && c < COLOR_COUNT) { return color_names[c]; } return "Unknown"; } // Function to get color value int color_value(enum Color c) { if (c >= 0 && c < COLOR_COUNT) { return color_values[c]; } return 0; } int main() { printf("Color enumeration:\n"); for (int i = 0; i < COLOR_COUNT; i++) { printf(" %d: %s (0x%06X)\n", i, color_name(i), color_value(i)); } // Use enum and look up values enum Color c = BLUE; printf("\nSelected color: %s = 0x%06X\n", color_name(c), color_value(c)); // Parse color by name const char *target = "GREEN"; for (int i = 0; i < COLOR_COUNT; i++) { if (strcmp(color_names[i], target) == 0) { printf("Found %s at index %d (0x%06X)\n", target, i, color_value(i)); break; } } return 0; }
Single definition, multiple expansions:
NAME_LIST once with X(...) for each entry#define X(...)Synchronization benefit:
Common use cases:
Dispatch table example:
#define HANDLERS_LIST \ X(CMD_START, handle_start) \ X(CMD_STOP, handle_stop) \ X(CMD_STATUS, handle_status) // Generate enum #define X(cmd, func) cmd, enum Command { HANDLERS_LIST }; #undef X // Generate handler table #define X(cmd, func) func, handler_t handlers[] = { HANDLERS_LIST }; #undef X
Variadic X usage:
X(name, ...) accepts multiple fieldsDebugging gotcha:
gcc -E to see expansionAlternatives:
Limitations:
Style considerations:
#undef X after each use to avoid pollutionCOLORS_LIST, not LIST) helpsModern alternatives: