Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Post History

66%
+2 −0
Q&A What are X macros and when to use them?

Purpose and use X macros is a design pattern used for the purpose of centralizing data & code maintenance to a single point in the program. Instead of maintaining code based on some data set i...

posted 3d ago by Lundin‭  ·  edited 3d ago by Lundin‭

Answer
#5: Post edited by user avatar Lundin‭ · 2025-02-25T15:37:44Z (3 days ago)
  • **Purpose and use**
  • X macros is a design pattern used for the purpose of centralizing data & code maintenance to a single point in the program. Instead of maintaining code based on some data set in multiple places, we can gather the data at one single place, in a macro. And it's all done at compile-time, so there's no run-time overhead.
  • The idea is that all data, for which we need to do a certain thing with repeatedly, is gathered in a list, the "X macro list". Each item is placed inside a parenthesized, comma-separated expression with all data that belongs together:
  • ```c
  • #define ANIMAL_LIST \
  • /*animal legs sound */ \
  • X(cat, 4, meow) \
  • X(dog, 4, woof) \
  • X(rooster, 2, cockadoodledoo) \
  • ```
  • Each item inside the comma-separated list should preferrably be a valid pre-processor token. We _could_ write each such item as multiple pre-processor tokens too, but that will restrict the flexibility somewhat.
  • Each X in this list will boil down to a function-like macro call. And so when we use `ANIMAL_LIST` there will be 3 macro calls immediately after each other. Since `ANIMAL_LIST` is one big macro we have to use `\` to separate the lines into something readable for humans.
  • In the first example from the question and from Wikipedia, they use the style of `#define X(...) something`, then call the macro `X` over and over. Then finally `#undef X` to make the macro name `X` usable again. Example:
  • ```c
  • #define X(animal, legs, sound) \
  • printf("A %s has %d legs and says '%s'.\n", #animal, legs, #sound);
  • ANIMAL_LIST /* note the lack of semicolon here */
  • #undef X
  • ```
  • Note that since this is still macros, we may use the `#` "stringification operator" to convert something to a string whenever we want. Or use `##` to concatenate symbols. The above example will result in a pre-processor output like:
  • ```c
  • printf("A %s has %d legs and says '%s'.\n", "cat", 4, "meow");
  • printf("A %s has %d legs and says '%s'.\n", "dog", 4, "woof");
  • printf("A %s has %d legs and says '%s'.\n", "rooster", 2, "cockadoodledoo");
  • ```
  • So we may use this for iterating across data, for generating enums, even for generating code.
  • ---
  • **Preferred style**
  • I strongly recommend a different style than the `#define X` / `#undef X` one though. As noted, the `#define X ... #undef X` adds a bit of extra clutter. But it also looks kind of alien to type out a macro name `ANIMAL_LIST` without any parenthesis or semicolon.
  • There is a different style that removes that clutter while at the same time making the X macros even more flexible. In the preferred style we pass the macro that should be called for each data item as a parameter to the list macro itself:
  • #define ANIMAL_LIST(X)
  • Now that's a bit confusing but more powerful than the previous. On the caller side we wouldn't use the generic identifier X any longer, but instead define a named macro:
  • #define ANIMAL_PRINT(animal, legs, sound) ...
  • And then call the list as
  • ANIMAL_LIST(ANIMAL_PRINT)
  • This gives less clutter and more self-documenting names. But now we may not just call a list of data with different macros - we may also use the macro for different sets of identical data.
  • MAMMAL_LIST(ANIMAL_PRINT)
  • BIRD_LIST(ANIMAL_PRINT)
  • An example where two data lists are used for printing of lots of diverse data:
  • ```c
  • #include <stdio.h>
  • #include <stdint.h>
  • #define DATA_LIST1(X) \
  • X(int, 123, %d) \
  • X(double, 3.1415, %f) \
  • X(char*, "hello", %s) \
  • #define DATA_LIST2(X) \
  • X(char, 'A', %c) \
  • X(size_t, SIZE_MAX, %zu) \
  • #define DATA_PRINT(type, val, fmt) printf("%s: " #fmt "\n", #type, val);
  • int main()
  • {
  • DATA_LIST1(DATA_PRINT)
  • DATA_LIST2(DATA_PRINT)
  • }
  • ```
  • ---
  • **Advanced example**
  • Here is another advanced example mostly just to demonstrate how ridiculously flexible these can get. (If you don't understand the following code then don't worry! It is using a lot of different C tricks all at once.)
  • Lets define one X macro list for all types we want to support and their respective `printf` format specifiers:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • ```
  • Then use this to generate code for 3 different functions, `print_int`, `print_double` and so on:
  • ```c
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • ```
  • Here the token concatenation operator `##` was used, which wouldn't be possible if we had items in the list consisting of more than one pre-processor token, for example `char*`. Nor would it be possible if the list contained items which can't exist in a C identifier, `print_char*` would be invalid syntax.
  • Next up suppose we have a list of data:
  • ```c
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • ```
  • And we wish to print this with a generic function interface `print()`, no matter which data we put in, to call the various type-specific functions defined earlier. Such a function call could be made type safe with for example:
  • #define print(val) _Generic(val, int: print_int) (val)
  • And then just add `double: print_double` etc for each supported type. But typing that manually is _too easy_ :) And not generic enough - why not use our supported types X macro for the generic association list itself:
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • Note that the `fmt` parameter of the X macro was completely ignored, which is something we can always chose to do. Also in the specific case of `_Generic`, it doesn't support trailing comma, hence the peculiar syntax with `,` first.
  • And finally, to completely soak ourselves in X macros, lets call that generic `print` macro for each item in our data list:
  • ```c
  • #include <stdio.h>
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • int main()
  • {
  • #define PRINT_LIST(val) print(val);
  • DATA_LIST(PRINT_LIST)
  • }
  • ```
  • https://godbolt.org/z/cq7YarM66 (note how it just boils down to 3 printf calls with optimizations on)
  • Preprocessed code (re-formatted here for readability):
  • ```c
  • void print_int (int param)
  • {
  • printf("%d" "\n", param);
  • }
  • void print_double (double param)
  • {
  • printf("%f" "\n", param);
  • }
  • void print_char (char param)
  • {
  • printf("%c" "\n", param);
  • }
  • int main()
  • {
  • _Generic((123),
  • int: print_int,
  • double: print_double,
  • char: print_char) (123);
  • _Generic((3.1415),
  • int: print_int,
  • double: print_double,
  • char: print_char) (3.1415);
  • _Generic(((char)'A'),
  • int: print_int,
  • double: print_double,
  • char: print_char) ((char)'A');
  • }
  • ```
  • ---
  • **Advantages of X macros:**
  • - Very generic and powerful. Data can be turned into identifiers and/or strings as needed.
  • - Reduces code repetition drastically.
  • - Ideal for reducing code maintenance to a single place in the code base.
  • - Ideal for maintaining some old code base where someone already made a fine mess and we need to bring order to it.
  • - A de facto industry standard way of writing macros, instead of cooking up some home-brewed, project-specific macro solutions (very bad practice).
  • **Disadvantages of X macros:**
  • - Hard to read, especially for those not used to seeing them.
  • - Hard to trouble-shoot while writing them.
  • - Although easy to maintain on the code side, they are hard to debug in a debugger - similar to attempting to debugging for example inlined functions.
  • **Purpose and use**
  • X macros is a design pattern used for the purpose of centralizing data & code maintenance to a single point in the program. Instead of maintaining code based on some data set in multiple places, we can gather the data at one single place, in a macro. And it's all done at compile-time, so there's no run-time overhead.
  • The idea is that all data, for which we need to do a certain thing with repeatedly, is gathered in a list, the "X macro list". Each item is placed inside a parenthesized, comma-separated expression with all data that belongs together:
  • ```c
  • #define ANIMAL_LIST \
  • /*animal legs sound */ \
  • X(cat, 4, meow) \
  • X(dog, 4, woof) \
  • X(rooster, 2, cockadoodledoo) \
  • ```
  • Each item inside the comma-separated list should preferrably be a valid pre-processor token. We _could_ write each such item as multiple pre-processor tokens too, but that will restrict the flexibility somewhat.
  • Each X in this list will boil down to a function-like macro call. And so when we use `ANIMAL_LIST` there will be 3 macro calls immediately after each other. Since `ANIMAL_LIST` is one big macro we have to use `\` to separate the lines into something readable for humans.
  • In the first example from the question and from Wikipedia, they use the style of `#define X(...) something`, then call the macro `X` over and over. Then finally `#undef X` to make the macro name `X` usable again. Example:
  • ```c
  • #define X(animal, legs, sound) \
  • printf("A %s has %d legs and says '%s'.\n", #animal, legs, #sound);
  • ANIMAL_LIST /* note the lack of semicolon here */
  • #undef X
  • ```
  • Note that since this is still macros, we may use the `#` "stringification operator" to convert something to a string whenever we want. Or use `##` to concatenate symbols. The above example will result in a pre-processor output like:
  • ```c
  • printf("A %s has %d legs and says '%s'.\n", "cat", 4, "meow");
  • printf("A %s has %d legs and says '%s'.\n", "dog", 4, "woof");
  • printf("A %s has %d legs and says '%s'.\n", "rooster", 2, "cockadoodledoo");
  • ```
  • So we may use this for iterating across data, for generating enums, even for generating code.
  • ---
  • **Preferred style**
  • I strongly recommend a different style than the `#define X` / `#undef X` one though. As noted, the `#define X ... #undef X` adds a bit of extra clutter. But it also looks kind of alien to type out a macro name `ANIMAL_LIST` without any parenthesis or semicolon.
  • There is a different style that removes that clutter while at the same time making the X macros even more flexible. In the preferred style we pass the macro that should be called for each data item as a parameter to the list macro itself:
  • #define ANIMAL_LIST(X)
  • Now that's a bit confusing but more powerful than the previous. On the caller side we wouldn't use the generic identifier X any longer, but instead define a named macro:
  • #define ANIMAL_PRINT(animal, legs, sound) ...
  • And then call the list as
  • ANIMAL_LIST(ANIMAL_PRINT)
  • This gives less clutter and more self-documenting names. But now we may not just call a list of data with different macros - we may also use the macro for different sets of identical data.
  • MAMMAL_LIST(ANIMAL_PRINT)
  • BIRD_LIST(ANIMAL_PRINT)
  • An example where two data lists are used for printing of lots of diverse data:
  • ```c
  • #include <stdio.h>
  • #include <stdint.h>
  • #define DATA_LIST1(X) \
  • X(int, 123, %d) \
  • X(double, 3.1415, %f) \
  • X(char*, "hello", %s) \
  • #define DATA_LIST2(X) \
  • X(char, 'A', %c) \
  • X(size_t, SIZE_MAX, %zu) \
  • #define DATA_PRINT(type, val, fmt) printf("%s: " #fmt "\n", #type, val);
  • int main()
  • {
  • DATA_LIST1(DATA_PRINT)
  • DATA_LIST2(DATA_PRINT)
  • }
  • ```
  • ---
  • **Advanced example**
  • Here is another advanced example mostly just to demonstrate how ridiculously flexible these can get. (If you don't understand the following code then don't worry! It is using a lot of different C tricks all at once.)
  • Lets define one X macro list for all types we want to support and their respective `printf` format specifiers:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • ```
  • Then use this to generate code for 3 different functions, `print_int`, `print_double` and so on:
  • ```c
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • ```
  • Here the token concatenation operator `##` was used, which wouldn't be possible if we had items in the list consisting of more than one pre-processor token, for example `char*`. Nor would it be possible if the list contained items which can't exist in a C identifier, `print_char*` would be invalid syntax.
  • Next up suppose we have a list of data:
  • ```c
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • ```
  • And we wish to print this with a generic function interface `print()`, no matter which data we put in, to call the various type-specific functions defined earlier. Such a function call could be made type safe with for example:
  • #define print(val) _Generic(val, int: print_int) (val)
  • And then just add `double: print_double` etc for each supported type. But typing that manually is _too easy_ :) And not generic enough - why not use our supported types X macro for the generic association list itself:
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • Note that the `fmt` parameter of the X macro was completely ignored, which is something we can always chose to do. Also in the specific case of `_Generic`, it doesn't support trailing comma, hence the peculiar syntax with `,` first.
  • And finally, to completely soak ourselves in X macros, lets call that generic `print` macro for each item in our data list:
  • ```c
  • #include <stdio.h>
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • int main()
  • {
  • #define PRINT_LIST(val) print(val);
  • DATA_LIST(PRINT_LIST)
  • }
  • ```
  • https://godbolt.org/z/cq7YarM66 (note how it just boils down to 3 printf calls with optimizations on)
  • Preprocessed code (re-formatted here for readability):
  • ```c
  • void print_int (int param)
  • {
  • printf("%d" "\n", param);
  • }
  • void print_double (double param)
  • {
  • printf("%f" "\n", param);
  • }
  • void print_char (char param)
  • {
  • printf("%c" "\n", param);
  • }
  • int main()
  • {
  • _Generic((123),
  • int: print_int,
  • double: print_double,
  • char: print_char) (123);
  • _Generic((3.1415),
  • int: print_int,
  • double: print_double,
  • char: print_char) (3.1415);
  • _Generic(((char)'A'),
  • int: print_int,
  • double: print_double,
  • char: print_char) ((char)'A');
  • }
  • ```
  • ---
  • **Advantages of X macros:**
  • - Very generic and powerful. Data can be turned into identifiers and/or strings as needed.
  • - Reduces code repetition drastically.
  • - Ideal for reducing code maintenance to a single place in the code base.
  • - Ideal for maintaining some old code base where someone already made a fine mess and we need to bring order to it.
  • - A de facto industry standard way of writing macros, instead of cooking up some home-brewed, project-specific macro solutions (very bad practice).
  • **Disadvantages of X macros:**
  • - Hard to read, especially for those not used to seeing them.
  • - Hard to trouble-shoot while writing them.
  • - Although easy to maintain on the code side, they are hard to debug in a debugger - similar to attempting to debugging for example inlined functions.
  • I always recommend to use X macros as a _last resort_, rather than the first thing that comes to mind. For example maintaining a bunch of array tables with related data and a common index used to tie that data together is very readable and reasonably fast as well.
#4: Post edited by user avatar Lundin‭ · 2025-02-25T15:34:03Z (3 days ago)
  • **Purpose and use**
  • X macros is a design pattern used for the purpose of centralizing data & code maintenance to a single point in the program. Instead of maintaining code based on some data set in multiple places, we can gather the data at one single place, in a macro. And it's all done at compile-time, so there's no run-time overhead.
  • The idea is that all data, for which we need to do a certain thing with repeatedly, is gathered in a list, the "X macro list". Each item is placed inside a parenthesized, comma-separated expression with all data that belongs together:
  • ```c
  • #define ANIMAL_LIST \
  • /*animal legs sound */ \
  • X(cat, 4, meow) \
  • X(dog, 4, woof) \
  • X(rooster, 2, cockadoodledoo) \
  • ```
  • Each item inside the comma-separated list should preferrably be a valid pre-processor token. We _could_ write each such item as multiple pre-processor tokens too, but that will restrict the flexibility somewhat.
  • Each X in this list will boil down to a function-like macro call. And so when we use `ANIMAL_LIST` there will be 3 macro calls immediately after each other. Since `ANIMAL_LIST` is one big macro we have to use `\` to separate the lines into something readable for humans.
  • In the first example from the question and from Wikipedia, they use the style of `#define X(...) something`, then call the macro `X` over and over. Then finally `#undef X` to make the macro name `X` usable again. Example:
  • ```c
  • #define X(animal, legs, sound) \
  • printf("A %s has %d legs and says '%s'.\n", #animal, legs, #sound);
  • ANIMAL_LIST /* note the lack of semicolon here */
  • #undef X
  • ```
  • Note that since this is still macros, we may use the `#` "stringification operator" to convert something to a string whenever we want. Or use `##` to concatenate symbols. The above example will result in a pre-processor output like:
  • ```c
  • printf("A %s has %d legs and says '%s'.\n", "cat", 4, "meow");
  • printf("A %s has %d legs and says '%s'.\n", "dog", 4, "woof");
  • printf("A %s has %d legs and says '%s'.\n", "rooster", 2, "cockadoodledoo");
  • ```
  • So we may use this for iterating across data, for generating enums, even for generating code.
  • ---
  • **Preferred style**
  • I strongly recommend a different style than the `#define X` / `#undef X` one though. As noted, the `#define X ... #undef X` adds a bit of extra clutter. But it also looks kind of alien to type out a macro name `ANIMAL_LIST` without any parenthesis or semicolon.
  • There is a different style that removes that clutter while at the same time making the X macros even more flexible. In the preferred style we pass the macro that should be called for each data item as a parameter to the list macro itself:
  • #define ANIMAL_LIST(X)
  • Now that's a bit confusing but more powerful than the previous. On the caller side we wouldn't use the generic identifier X any longer, but instead define a named macro:
  • #define ANIMAL_PRINT(animal, legs, sound) ...
  • And then call the list as
  • ANIMAL_LIST(ANIMAL_PRINT)
  • This gives less clutter and more self-documenting names. But now we may not just call a list of data with different macros - we may also use the macro for different sets of identical data.
  • MAMMAL_LIST(ANIMAL_PRINT)
  • BIRD_LIST(ANIMAL_PRINT)
  • An example where two data lists are used for printing of lots of diverse data:
  • ```c
  • #include <stdio.h>
  • #include <stdint.h>
  • #define DATA_LIST1(X) \
  • X(int, 123, %d) \
  • X(double, 3.1415, %f) \
  • X(char*, "hello", %s) \
  • #define DATA_LIST2(X) \
  • X(char, 'A', %c) \
  • X(size_t, SIZE_MAX, %zu) \
  • #define DATA_PRINT(type, val, fmt) printf("%s: " #fmt "\n", #type, val);
  • int main()
  • {
  • DATA_LIST1(DATA_PRINT)
  • DATA_LIST2(DATA_PRINT)
  • }
  • ```
  • ---
  • **Advanced example**
  • Here is another advanced example mostly just to demonstrate how ridiculously flexible these can get. (If you don't understand the following code then don't worry! It is using a lot of different C tricks all at once.)
  • Lets define one X macro list for all types we want to support and their respective `printf` format specifiers:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • ```
  • Then use this to generate code for 3 different functions, `print_int`, `print_double` and so on:
  • ```c
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • ```
  • Here the token concatenation operator `##` was used, which wouldn't be possible if we had items in the list consisting of more than one pre-processor token, for example `char*`. Nor would it be possible if the list contained items which can't exist in a C identifier, `print_char*` would be invalid syntax.
  • Next up suppose we have a list of data:
  • ```c
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • ```
  • And we wish to print this with a generic function interface `print()`, no matter which data we put in, to call the various type-specific functions defined earlier. Such a function call could be made type safe with for example:
  • #define print(val) _Generic(val, int: print_int) (val)
  • And then just add `double: print_double` etc for each supported type. But typing that manually is _too easy_ :) And note generic enough - why not use our supported types X macro for the generic association list itself:
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • Note that the `fmt` parameter of the X macro was completely ignored, which is something we can always chose to do. Also in the specific case of `_Generic`, it doesn't support trailing comma, hence the peculiar syntax with `,` first.
  • And finally, to completely soak ourselves in X macros, lets call that generic `print` macro for each item in our data list:
  • ```c
  • #include <stdio.h>
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • int main()
  • {
  • #define PRINT_LIST(val) print(val);
  • DATA_LIST(PRINT_LIST)
  • }
  • ```
  • https://godbolt.org/z/cq7YarM66 (note how it just boils down to 3 printf calls with optimizations on)
  • Preprocessed code (re-formatted here for readability):
  • ```c
  • void print_int (int param)
  • {
  • printf("%d" "\n", param);
  • }
  • void print_double (double param)
  • {
  • printf("%f" "\n", param);
  • }
  • void print_char (char param)
  • {
  • printf("%c" "\n", param);
  • }
  • int main()
  • {
  • _Generic((123),
  • int: print_int,
  • double: print_double,
  • char: print_char) (123);
  • _Generic((3.1415),
  • int: print_int,
  • double: print_double,
  • char: print_char) (3.1415);
  • _Generic(((char)'A'),
  • int: print_int,
  • double: print_double,
  • char: print_char) ((char)'A');
  • }
  • ```
  • ---
  • **Advantages of X macros:**
  • - Very generic and powerful. Data can be turned into identifiers and/or strings as needed.
  • - Reduces code repetition drastically.
  • - Ideal for reducing code maintenance to a single place in the code base.
  • - Ideal for maintaining some old code base where someone already made a fine mess and we need to bring order to it.
  • - A de facto industry standard way of writing macros, instead of cooking up some home-brewed, project-specific macro solutions (very bad practice).
  • **Disadvantages of X macros:**
  • - Hard to read, especially for those not used to seeing them.
  • - Hard to trouble-shoot while writing them.
  • - Although easy to maintain on the code side, they are hard to debug in a debugger - similar to attempting to debugging for example inlined functions.
  • **Purpose and use**
  • X macros is a design pattern used for the purpose of centralizing data & code maintenance to a single point in the program. Instead of maintaining code based on some data set in multiple places, we can gather the data at one single place, in a macro. And it's all done at compile-time, so there's no run-time overhead.
  • The idea is that all data, for which we need to do a certain thing with repeatedly, is gathered in a list, the "X macro list". Each item is placed inside a parenthesized, comma-separated expression with all data that belongs together:
  • ```c
  • #define ANIMAL_LIST \
  • /*animal legs sound */ \
  • X(cat, 4, meow) \
  • X(dog, 4, woof) \
  • X(rooster, 2, cockadoodledoo) \
  • ```
  • Each item inside the comma-separated list should preferrably be a valid pre-processor token. We _could_ write each such item as multiple pre-processor tokens too, but that will restrict the flexibility somewhat.
  • Each X in this list will boil down to a function-like macro call. And so when we use `ANIMAL_LIST` there will be 3 macro calls immediately after each other. Since `ANIMAL_LIST` is one big macro we have to use `\` to separate the lines into something readable for humans.
  • In the first example from the question and from Wikipedia, they use the style of `#define X(...) something`, then call the macro `X` over and over. Then finally `#undef X` to make the macro name `X` usable again. Example:
  • ```c
  • #define X(animal, legs, sound) \
  • printf("A %s has %d legs and says '%s'.\n", #animal, legs, #sound);
  • ANIMAL_LIST /* note the lack of semicolon here */
  • #undef X
  • ```
  • Note that since this is still macros, we may use the `#` "stringification operator" to convert something to a string whenever we want. Or use `##` to concatenate symbols. The above example will result in a pre-processor output like:
  • ```c
  • printf("A %s has %d legs and says '%s'.\n", "cat", 4, "meow");
  • printf("A %s has %d legs and says '%s'.\n", "dog", 4, "woof");
  • printf("A %s has %d legs and says '%s'.\n", "rooster", 2, "cockadoodledoo");
  • ```
  • So we may use this for iterating across data, for generating enums, even for generating code.
  • ---
  • **Preferred style**
  • I strongly recommend a different style than the `#define X` / `#undef X` one though. As noted, the `#define X ... #undef X` adds a bit of extra clutter. But it also looks kind of alien to type out a macro name `ANIMAL_LIST` without any parenthesis or semicolon.
  • There is a different style that removes that clutter while at the same time making the X macros even more flexible. In the preferred style we pass the macro that should be called for each data item as a parameter to the list macro itself:
  • #define ANIMAL_LIST(X)
  • Now that's a bit confusing but more powerful than the previous. On the caller side we wouldn't use the generic identifier X any longer, but instead define a named macro:
  • #define ANIMAL_PRINT(animal, legs, sound) ...
  • And then call the list as
  • ANIMAL_LIST(ANIMAL_PRINT)
  • This gives less clutter and more self-documenting names. But now we may not just call a list of data with different macros - we may also use the macro for different sets of identical data.
  • MAMMAL_LIST(ANIMAL_PRINT)
  • BIRD_LIST(ANIMAL_PRINT)
  • An example where two data lists are used for printing of lots of diverse data:
  • ```c
  • #include <stdio.h>
  • #include <stdint.h>
  • #define DATA_LIST1(X) \
  • X(int, 123, %d) \
  • X(double, 3.1415, %f) \
  • X(char*, "hello", %s) \
  • #define DATA_LIST2(X) \
  • X(char, 'A', %c) \
  • X(size_t, SIZE_MAX, %zu) \
  • #define DATA_PRINT(type, val, fmt) printf("%s: " #fmt "\n", #type, val);
  • int main()
  • {
  • DATA_LIST1(DATA_PRINT)
  • DATA_LIST2(DATA_PRINT)
  • }
  • ```
  • ---
  • **Advanced example**
  • Here is another advanced example mostly just to demonstrate how ridiculously flexible these can get. (If you don't understand the following code then don't worry! It is using a lot of different C tricks all at once.)
  • Lets define one X macro list for all types we want to support and their respective `printf` format specifiers:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • ```
  • Then use this to generate code for 3 different functions, `print_int`, `print_double` and so on:
  • ```c
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • ```
  • Here the token concatenation operator `##` was used, which wouldn't be possible if we had items in the list consisting of more than one pre-processor token, for example `char*`. Nor would it be possible if the list contained items which can't exist in a C identifier, `print_char*` would be invalid syntax.
  • Next up suppose we have a list of data:
  • ```c
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • ```
  • And we wish to print this with a generic function interface `print()`, no matter which data we put in, to call the various type-specific functions defined earlier. Such a function call could be made type safe with for example:
  • #define print(val) _Generic(val, int: print_int) (val)
  • And then just add `double: print_double` etc for each supported type. But typing that manually is _too easy_ :) And not generic enough - why not use our supported types X macro for the generic association list itself:
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • Note that the `fmt` parameter of the X macro was completely ignored, which is something we can always chose to do. Also in the specific case of `_Generic`, it doesn't support trailing comma, hence the peculiar syntax with `,` first.
  • And finally, to completely soak ourselves in X macros, lets call that generic `print` macro for each item in our data list:
  • ```c
  • #include <stdio.h>
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • int main()
  • {
  • #define PRINT_LIST(val) print(val);
  • DATA_LIST(PRINT_LIST)
  • }
  • ```
  • https://godbolt.org/z/cq7YarM66 (note how it just boils down to 3 printf calls with optimizations on)
  • Preprocessed code (re-formatted here for readability):
  • ```c
  • void print_int (int param)
  • {
  • printf("%d" "\n", param);
  • }
  • void print_double (double param)
  • {
  • printf("%f" "\n", param);
  • }
  • void print_char (char param)
  • {
  • printf("%c" "\n", param);
  • }
  • int main()
  • {
  • _Generic((123),
  • int: print_int,
  • double: print_double,
  • char: print_char) (123);
  • _Generic((3.1415),
  • int: print_int,
  • double: print_double,
  • char: print_char) (3.1415);
  • _Generic(((char)'A'),
  • int: print_int,
  • double: print_double,
  • char: print_char) ((char)'A');
  • }
  • ```
  • ---
  • **Advantages of X macros:**
  • - Very generic and powerful. Data can be turned into identifiers and/or strings as needed.
  • - Reduces code repetition drastically.
  • - Ideal for reducing code maintenance to a single place in the code base.
  • - Ideal for maintaining some old code base where someone already made a fine mess and we need to bring order to it.
  • - A de facto industry standard way of writing macros, instead of cooking up some home-brewed, project-specific macro solutions (very bad practice).
  • **Disadvantages of X macros:**
  • - Hard to read, especially for those not used to seeing them.
  • - Hard to trouble-shoot while writing them.
  • - Although easy to maintain on the code side, they are hard to debug in a debugger - similar to attempting to debugging for example inlined functions.
#3: Post edited by user avatar Lundin‭ · 2025-02-25T15:32:34Z (3 days ago)
  • **Purpose and use**
  • X macros is a design pattern used for the purpose of centralizing data & code maintenance to a single point in the program. Instead of maintaining code based on some data set in multiple places, we can gather the data at one single place, in a macro. And it's all done at compile-time, so there's no run-time overhead.
  • The idea is that all data, for which we need to do a certain thing with repeatedly, is gathered in a list, the "X macro list". Each item is placed inside a parenthesized, comma-separated expression with all data that belongs together:
  • ```c
  • #define ANIMAL_LIST \
  • /*animal legs sound */ \
  • X(cat, 4, meow) \
  • X(dog, 4, woof) \
  • X(rooster, 2, cockadoodledoo) \
  • ```
  • Each item inside the comma-separated list should preferrably be a valid pre-processor token. We _could_ write each such item as multiple pre-processor tokens too, but that will restrict the flexibility somewhat.
  • Each X in this list will boil down to a function-like macro call. And so when we use `ANIMAL_LIST` there will be 3 macro calls immediately after each other. Since `ANIMAL_LIST` is one big macro we have to use `\` to separate the lines into something readable for humans.
  • In the first example from the question and from Wikipedia, they use the style of `#define X(...) something`, then call the macro `X` over and over. Then finally `#undef X` to make the macro name `X` usable again. Example:
  • ```c
  • #define X(animal, legs, sound) \
  • printf("A %s has %d legs and says '%s'.\n", #animal, legs, #sound);
  • ANIMAL_LIST /* note the lack of semicolon here */
  • #undef X
  • ```
  • Note that since this is still macros, we may use the `#` "stringification operator" to convert something to a string whenever we want. Or use `##` to concatenate symbols. The above example will result in a pre-processor output like:
  • ```c
  • printf("A %s has %d legs and says '%s'.\n", "cat", 4, "meow");
  • printf("A %s has %d legs and says '%s'.\n", "dog", 4, "woof");
  • printf("A %s has %d legs and says '%s'.\n", "rooster", 2, "cockadoodledoo");
  • ```
  • So we may use this for iterating across data, for generating enums, even for generating code.
  • ---
  • **Preferred style**
  • I strongly recommend a different style than the `#define X` / `#undef X` one though. As noted, the `#define X ... #undef X` adds a bit of extra clutter. But it also looks kind of alien to type out a macro name `ANIMAL_LIST` without any parenthesis or semicolon.
  • There is a different style that removes that clutter while at the same time making the X macros even more flexible. In the preferred style we pass the macro that should be called for each data item as a parameter to the list macro itself:
  • #define ANIMAL_LIST(X)
  • Now that's a bit confusing but more powerful than the previous. On the caller side we wouldn't use the generic identifier X any longer, but instead define a named macro:
  • #define ANIMAL_PRINT(animal, legs, sound) ...
  • And then call the list as
  • ANIMAL_LIST(ANIMAL_PRINT)
  • This gives less clutter and more self-documenting names. But now we may not just call a list of data with different macros - we may also use the macro for different sets of identical data.
  • MAMMAL_LIST(ANIMAL_PRINT)
  • BIRD_LIST(ANIMAL_PRINT)
  • An example where two data lists are used for printing of lots of diverse data:
  • ```c
  • #include <stdio.h>
  • #include <stdint.h>
  • #define DATA_LIST1(X) \
  • X(int, 123, %d) \
  • X(double, 3.1415, %f) \
  • X(char*, "hello", %s) \
  • #define DATA_LIST2(X) \
  • X(char, 'A', %c) \
  • X(size_t, SIZE_MAX, %zu) \
  • #define DATA_PRINT(type, val, fmt) printf("%s: " #fmt "\n", #type, val);
  • int main()
  • {
  • DATA_LIST1(DATA_PRINT)
  • DATA_LIST2(DATA_PRINT)
  • }
  • ```
  • ---
  • **Advanced example**
  • Here is another advanced example mostly just to demonstrate how ridiculously flexible these can get. (If you don't understand the following code then don't worry! It is using a lot of different C tricks all at once.)
  • Lets define one X macro list for all types we want to support and their respective `printf` format specifiers:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • ```
  • Then use this to generate code for 3 different functions, `print_int`, `print_double` and so on:
  • ```c
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • ```
  • Here the token concatenation operator `##` was used, which wouldn't be possible if we had items in the list consisting of more than one pre-processor token, for example `char*`. Nor would it be possible if the list contained items which can't exist in a C identifier, `print_char*` would be invalid syntax.
  • Next up suppose we have a list of data:
  • ```c
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • ```
  • And we wish to print this with a generic function interface `print()`, no matter which data we put in, to call the various type-specific functions defined earlier. Such a function call could be made type safe with for example:
  • #define print(val) _Generic(val, int: print_int) (val)
  • And then just add `double: print_double` etc for each supported type. But typing that manually is _too easy_ :) And note generic enough - why not use our supported types X macro for the generic association list itself:
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • Note that the `fmt` parameter of the X macro was completely ignored, which is something we can always chose to do. Also in the specific case of `_Generic`, it doesn't support trailing comma, hence the peculiar syntax with `,` first.
  • And finally, to completely soak ourselves in X macros, lets call that generic `print` macro for each item in our data list:
  • ```c
  • #include <stdio.h>
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • int main()
  • {
  • #define PRINT_LIST(val) print(val);
  • DATA_LIST(PRINT_LIST)
  • }
  • ```
  • https://godbolt.org/z/cq7YarM66
  • Preprocessed code (re-formatted here for readability):
  • ```c
  • void print_int (int param)
  • {
  • printf("%d" "\n", param);
  • }
  • void print_double (double param)
  • {
  • printf("%f" "\n", param);
  • }
  • void print_char (char param)
  • {
  • printf("%c" "\n", param);
  • }
  • int main()
  • {
  • _Generic((123),
  • int: print_int,
  • double: print_double,
  • char: print_char) (123);
  • _Generic((3.1415),
  • int: print_int,
  • double: print_double,
  • char: print_char) (3.1415);
  • _Generic(((char)'A'),
  • int: print_int,
  • double: print_double,
  • char: print_char) ((char)'A');
  • }
  • ```
  • ---
  • **Advantages of X macros:**
  • - Very generic and powerful. Data can be turned into identifiers and/or strings as needed.
  • - Reduces code repetition drastically.
  • - Ideal for reducing code maintenance to a single place in the code base.
  • - Ideal for maintaining some old code base where someone already made a fine mess and we need to bring order to it.
  • - A de facto industry standard way of writing macros, instead of cooking up some home-brewed, project-specific macro solutions (very bad practice).
  • **Disadvantages of X macros:**
  • - Hard to read, especially for those not used to seeing them.
  • - Hard to trouble-shoot while writing them.
  • - Although easy to maintain on the code side, they are hard to debug in a debugger - similar to attempting to debugging for example inlined functions.
  • **Purpose and use**
  • X macros is a design pattern used for the purpose of centralizing data & code maintenance to a single point in the program. Instead of maintaining code based on some data set in multiple places, we can gather the data at one single place, in a macro. And it's all done at compile-time, so there's no run-time overhead.
  • The idea is that all data, for which we need to do a certain thing with repeatedly, is gathered in a list, the "X macro list". Each item is placed inside a parenthesized, comma-separated expression with all data that belongs together:
  • ```c
  • #define ANIMAL_LIST \
  • /*animal legs sound */ \
  • X(cat, 4, meow) \
  • X(dog, 4, woof) \
  • X(rooster, 2, cockadoodledoo) \
  • ```
  • Each item inside the comma-separated list should preferrably be a valid pre-processor token. We _could_ write each such item as multiple pre-processor tokens too, but that will restrict the flexibility somewhat.
  • Each X in this list will boil down to a function-like macro call. And so when we use `ANIMAL_LIST` there will be 3 macro calls immediately after each other. Since `ANIMAL_LIST` is one big macro we have to use `\` to separate the lines into something readable for humans.
  • In the first example from the question and from Wikipedia, they use the style of `#define X(...) something`, then call the macro `X` over and over. Then finally `#undef X` to make the macro name `X` usable again. Example:
  • ```c
  • #define X(animal, legs, sound) \
  • printf("A %s has %d legs and says '%s'.\n", #animal, legs, #sound);
  • ANIMAL_LIST /* note the lack of semicolon here */
  • #undef X
  • ```
  • Note that since this is still macros, we may use the `#` "stringification operator" to convert something to a string whenever we want. Or use `##` to concatenate symbols. The above example will result in a pre-processor output like:
  • ```c
  • printf("A %s has %d legs and says '%s'.\n", "cat", 4, "meow");
  • printf("A %s has %d legs and says '%s'.\n", "dog", 4, "woof");
  • printf("A %s has %d legs and says '%s'.\n", "rooster", 2, "cockadoodledoo");
  • ```
  • So we may use this for iterating across data, for generating enums, even for generating code.
  • ---
  • **Preferred style**
  • I strongly recommend a different style than the `#define X` / `#undef X` one though. As noted, the `#define X ... #undef X` adds a bit of extra clutter. But it also looks kind of alien to type out a macro name `ANIMAL_LIST` without any parenthesis or semicolon.
  • There is a different style that removes that clutter while at the same time making the X macros even more flexible. In the preferred style we pass the macro that should be called for each data item as a parameter to the list macro itself:
  • #define ANIMAL_LIST(X)
  • Now that's a bit confusing but more powerful than the previous. On the caller side we wouldn't use the generic identifier X any longer, but instead define a named macro:
  • #define ANIMAL_PRINT(animal, legs, sound) ...
  • And then call the list as
  • ANIMAL_LIST(ANIMAL_PRINT)
  • This gives less clutter and more self-documenting names. But now we may not just call a list of data with different macros - we may also use the macro for different sets of identical data.
  • MAMMAL_LIST(ANIMAL_PRINT)
  • BIRD_LIST(ANIMAL_PRINT)
  • An example where two data lists are used for printing of lots of diverse data:
  • ```c
  • #include <stdio.h>
  • #include <stdint.h>
  • #define DATA_LIST1(X) \
  • X(int, 123, %d) \
  • X(double, 3.1415, %f) \
  • X(char*, "hello", %s) \
  • #define DATA_LIST2(X) \
  • X(char, 'A', %c) \
  • X(size_t, SIZE_MAX, %zu) \
  • #define DATA_PRINT(type, val, fmt) printf("%s: " #fmt "\n", #type, val);
  • int main()
  • {
  • DATA_LIST1(DATA_PRINT)
  • DATA_LIST2(DATA_PRINT)
  • }
  • ```
  • ---
  • **Advanced example**
  • Here is another advanced example mostly just to demonstrate how ridiculously flexible these can get. (If you don't understand the following code then don't worry! It is using a lot of different C tricks all at once.)
  • Lets define one X macro list for all types we want to support and their respective `printf` format specifiers:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • ```
  • Then use this to generate code for 3 different functions, `print_int`, `print_double` and so on:
  • ```c
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • ```
  • Here the token concatenation operator `##` was used, which wouldn't be possible if we had items in the list consisting of more than one pre-processor token, for example `char*`. Nor would it be possible if the list contained items which can't exist in a C identifier, `print_char*` would be invalid syntax.
  • Next up suppose we have a list of data:
  • ```c
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • ```
  • And we wish to print this with a generic function interface `print()`, no matter which data we put in, to call the various type-specific functions defined earlier. Such a function call could be made type safe with for example:
  • #define print(val) _Generic(val, int: print_int) (val)
  • And then just add `double: print_double` etc for each supported type. But typing that manually is _too easy_ :) And note generic enough - why not use our supported types X macro for the generic association list itself:
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • Note that the `fmt` parameter of the X macro was completely ignored, which is something we can always chose to do. Also in the specific case of `_Generic`, it doesn't support trailing comma, hence the peculiar syntax with `,` first.
  • And finally, to completely soak ourselves in X macros, lets call that generic `print` macro for each item in our data list:
  • ```c
  • #include <stdio.h>
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • int main()
  • {
  • #define PRINT_LIST(val) print(val);
  • DATA_LIST(PRINT_LIST)
  • }
  • ```
  • https://godbolt.org/z/cq7YarM66 (note how it just boils down to 3 printf calls with optimizations on)
  • Preprocessed code (re-formatted here for readability):
  • ```c
  • void print_int (int param)
  • {
  • printf("%d" "\n", param);
  • }
  • void print_double (double param)
  • {
  • printf("%f" "\n", param);
  • }
  • void print_char (char param)
  • {
  • printf("%c" "\n", param);
  • }
  • int main()
  • {
  • _Generic((123),
  • int: print_int,
  • double: print_double,
  • char: print_char) (123);
  • _Generic((3.1415),
  • int: print_int,
  • double: print_double,
  • char: print_char) (3.1415);
  • _Generic(((char)'A'),
  • int: print_int,
  • double: print_double,
  • char: print_char) ((char)'A');
  • }
  • ```
  • ---
  • **Advantages of X macros:**
  • - Very generic and powerful. Data can be turned into identifiers and/or strings as needed.
  • - Reduces code repetition drastically.
  • - Ideal for reducing code maintenance to a single place in the code base.
  • - Ideal for maintaining some old code base where someone already made a fine mess and we need to bring order to it.
  • - A de facto industry standard way of writing macros, instead of cooking up some home-brewed, project-specific macro solutions (very bad practice).
  • **Disadvantages of X macros:**
  • - Hard to read, especially for those not used to seeing them.
  • - Hard to trouble-shoot while writing them.
  • - Although easy to maintain on the code side, they are hard to debug in a debugger - similar to attempting to debugging for example inlined functions.
#2: Post edited by user avatar Lundin‭ · 2025-02-25T15:26:22Z (3 days ago)
  • **Purpose and use**
  • X macros is a design pattern used for the purpose of centralizing data & code maintenance to a single point in the program. Instead of maintaining code based on some data set in multiple places, we can gather the data at one single place, in a macro. And it's all done at compile-time, so there's no run-time overhead.
  • The idea is that all data, for which we need to do a certain thing with repeatedly, is gathered in a list, the "X macro list". Each item is placed inside a parenthesized, comma-separated expression with all data that belongs together:
  • ```c
  • #define ANIMAL_LIST \
  • /*animal legs sound */ \
  • X(cat, 4, meow) \
  • X(dog, 4, woof) \
  • X(rooster, 2, cockadoodledoo) \
  • ```
  • Each item inside the comma-separated list should preferrably be a valid pre-processor token. We _could_ write each such item as multiple pre-processor tokens too, but that will restrict the flexibility somewhat.
  • Each X in this list will boil down to a function-like macro call. And so when we use `ANIMAL_LIST` there will be 3 macro calls immediately after each other. Since `ANIMAL_LIST` is one big macro we have to use `\` to separate the lines into something readable for humans.
  • In the first example from the question and from Wikipedia, they use the style of `#define X(...) something`, then call the macro `X` over and over. Then finally `#undef X` to make the macro name `X` usable again. Example:
  • ```c
  • #define X(animal, legs, sound) \
  • printf("A %s has %d legs and says '%s'.\n", #animal, legs, #sound);
  • ANIMAL_LIST /* note the lack of semicolon here */
  • #undef X
  • ```
  • Note that since this is still macros, we may use the `#` "stringification operator" to convert something to a string whenever we want. Or use `##` to concatenate symbols. The above example will result in a pre-processor output like:
  • ```c
  • printf("A %s has %d legs and says '%s'.\n", "cat", 4, "meow");
  • printf("A %s has %d legs and says '%s'.\n", "dog", 4, "woof");
  • printf("A %s has %d legs and says '%s'.\n", "rooster", 2, "cockadoodledoo");
  • ```
  • So we may use this for iterating across data, for generating enums, even for generating code.
  • ---
  • **Preferred style**
  • I strongly recommend a different style than the `#define X` / `#undef X` one though. As noted, the `#define X ... #undef X` adds a bit of extra clutter. But it also looks kind of alien to type out a macro name `ANIMAL_LIST` without any parenthesis or semicolon.
  • There is a different style that removes that clutter while at the same time making the X macros even more flexible. In the preferred style we pass the macro that should be called for each data item as a parameter to the list macro itself:
  • #define ANIMAL_LIST(X)
  • Now that's a bit confusing but more powerful than the previous. On the caller side we wouldn't use the generic identifier X any longer, but instead define a named macro:
  • #define ANIMAL_PRINT(animal, legs, sound) ...
  • And then call the list as
  • ANIMAL_LIST(ANIMAL_PRINT)
  • This gives less clutter and more self-documenting names. But now we may not just call a list of data with different macros - we may also use the macro for different sets of identical data.
  • MAMMAL_LIST(ANIMAL_PRINT)
  • BIRD_LIST(ANIMAL_PRINT)
  • An example where two data lists are used for printing of lots of diverse data:
  • ```c
  • #include <stdio.h>
  • #include <stdint.h>
  • #define DATA_LIST1(X) \
  • X(int, 123, %d) \
  • X(double, 3.1415, %f) \
  • X(char*, "hello", %s) \
  • #define DATA_LIST2(X) \
  • X(char, 'A', %c) \
  • X(size_t, SIZE_MAX, %zu) \
  • #define DATA_PRINT(type, val, fmt) printf("%s: " #fmt "\n", #type, val);
  • int main()
  • {
  • DATA_LIST1(DATA_PRINT)
  • DATA_LIST2(DATA_PRINT)
  • }
  • ```
  • ---
  • **Advanced example**
  • Here is another advanced example mostly just to demonstrate how ridiculously flexible these can get. (If you don't understand the following code then don't worry! It is using a lot of different C tricks all at once.)
  • Lets define one X macro list for all types we want to support and their respective `printf` format specifiers:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • ```
  • Then use this to generate code for 3 different functions, `print_int`, `print_double` and so on:
  • ```c
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • ```
  • Here the token concatenation operator `##` was used, which wouldn't be possible if we had items in the list consisting of more than one pre-processor token, for example `char*`. Nor would it be possible if the list contained items which can't exist in a C identifier, `print_char*` would be invalid syntax.
  • Next up suppose we have a list of data:
  • ```c
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • ```
  • And we wish to print this with a generic function interface `print()`, no matter which data we put in, to call the various type-specific functions defined earlier. Such a function call could be made type safe with for example:
  • #define print(val) _Generic(val, int: print_int) (val)
  • And then just add `double: print_double` etc for each supported type. But typing that manually is _too easy_ :) And note generic enough - why not use our supported types X macro for the generic association list itself:
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • Note that the `fmt` parameter of the X macro was completely ignored, which is something we can always chose to do. Also in the specific case of `_Generic`, it doesn't support trailing comma, hence the peculiar syntax with `,` first.
  • And finally, to completely soak ourselves in X macros, lets call that generic `print` macro for each item in our data list:
  • ```c
  • #include <stdio.h>
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • int main()
  • {
  • #define PRINT_LIST(val) print(val);
  • DATA_LIST(PRINT_LIST)
  • }
  • ```
  • https://godbolt.org/z/cq7YarM66
  • Preprocessed code (re-formatted here for readability):
  • ```c
  • void print_int (int param)
  • {
  • printf("%d" "\n", param);
  • }
  • void print_double (double param)
  • {
  • printf("%f" "\n", param);
  • }
  • void print_char (char param)
  • {
  • printf("%c" "\n", param);
  • }
  • int main()
  • {
  • _Generic((123),
  • int: print_int,
  • double: print_double,
  • char: print_char) (123);
  • _Generic((3.1415),
  • int: print_int,
  • double: print_double,
  • char: print_char) (3.1415);
  • _Generic(((char)'A'),
  • int: print_int,
  • double: print_double,
  • char: print_char) ((char)'A');
  • }
  • ```
  • ---
  • Advantages of X macros:
  • - Very generic and powerful. Data can be turned into identifiers and/or strings as needed.
  • - Reduces code repetition drastically.
  • - Ideal for reducing code maintenance to a single place in the code base.
  • - Ideal for maintaining some old code base where someone already made a fine mess and we need to bring order to it.
  • - A de facto industry standard way of writing macros, instead of cooking up some home-brewed, project-specific macro solutions (very bad practice).
  • Disadvantages of X macros:
  • - Hard to read, especially for those not used to seeing them.
  • - Hard to trouble-shoot while writing them.
  • - Although easy to maintain on the code side, they are hard to debug in a debugger - similar to attempting to debugging for example inlined functions.
  • **Purpose and use**
  • X macros is a design pattern used for the purpose of centralizing data & code maintenance to a single point in the program. Instead of maintaining code based on some data set in multiple places, we can gather the data at one single place, in a macro. And it's all done at compile-time, so there's no run-time overhead.
  • The idea is that all data, for which we need to do a certain thing with repeatedly, is gathered in a list, the "X macro list". Each item is placed inside a parenthesized, comma-separated expression with all data that belongs together:
  • ```c
  • #define ANIMAL_LIST \
  • /*animal legs sound */ \
  • X(cat, 4, meow) \
  • X(dog, 4, woof) \
  • X(rooster, 2, cockadoodledoo) \
  • ```
  • Each item inside the comma-separated list should preferrably be a valid pre-processor token. We _could_ write each such item as multiple pre-processor tokens too, but that will restrict the flexibility somewhat.
  • Each X in this list will boil down to a function-like macro call. And so when we use `ANIMAL_LIST` there will be 3 macro calls immediately after each other. Since `ANIMAL_LIST` is one big macro we have to use `\` to separate the lines into something readable for humans.
  • In the first example from the question and from Wikipedia, they use the style of `#define X(...) something`, then call the macro `X` over and over. Then finally `#undef X` to make the macro name `X` usable again. Example:
  • ```c
  • #define X(animal, legs, sound) \
  • printf("A %s has %d legs and says '%s'.\n", #animal, legs, #sound);
  • ANIMAL_LIST /* note the lack of semicolon here */
  • #undef X
  • ```
  • Note that since this is still macros, we may use the `#` "stringification operator" to convert something to a string whenever we want. Or use `##` to concatenate symbols. The above example will result in a pre-processor output like:
  • ```c
  • printf("A %s has %d legs and says '%s'.\n", "cat", 4, "meow");
  • printf("A %s has %d legs and says '%s'.\n", "dog", 4, "woof");
  • printf("A %s has %d legs and says '%s'.\n", "rooster", 2, "cockadoodledoo");
  • ```
  • So we may use this for iterating across data, for generating enums, even for generating code.
  • ---
  • **Preferred style**
  • I strongly recommend a different style than the `#define X` / `#undef X` one though. As noted, the `#define X ... #undef X` adds a bit of extra clutter. But it also looks kind of alien to type out a macro name `ANIMAL_LIST` without any parenthesis or semicolon.
  • There is a different style that removes that clutter while at the same time making the X macros even more flexible. In the preferred style we pass the macro that should be called for each data item as a parameter to the list macro itself:
  • #define ANIMAL_LIST(X)
  • Now that's a bit confusing but more powerful than the previous. On the caller side we wouldn't use the generic identifier X any longer, but instead define a named macro:
  • #define ANIMAL_PRINT(animal, legs, sound) ...
  • And then call the list as
  • ANIMAL_LIST(ANIMAL_PRINT)
  • This gives less clutter and more self-documenting names. But now we may not just call a list of data with different macros - we may also use the macro for different sets of identical data.
  • MAMMAL_LIST(ANIMAL_PRINT)
  • BIRD_LIST(ANIMAL_PRINT)
  • An example where two data lists are used for printing of lots of diverse data:
  • ```c
  • #include <stdio.h>
  • #include <stdint.h>
  • #define DATA_LIST1(X) \
  • X(int, 123, %d) \
  • X(double, 3.1415, %f) \
  • X(char*, "hello", %s) \
  • #define DATA_LIST2(X) \
  • X(char, 'A', %c) \
  • X(size_t, SIZE_MAX, %zu) \
  • #define DATA_PRINT(type, val, fmt) printf("%s: " #fmt "\n", #type, val);
  • int main()
  • {
  • DATA_LIST1(DATA_PRINT)
  • DATA_LIST2(DATA_PRINT)
  • }
  • ```
  • ---
  • **Advanced example**
  • Here is another advanced example mostly just to demonstrate how ridiculously flexible these can get. (If you don't understand the following code then don't worry! It is using a lot of different C tricks all at once.)
  • Lets define one X macro list for all types we want to support and their respective `printf` format specifiers:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • ```
  • Then use this to generate code for 3 different functions, `print_int`, `print_double` and so on:
  • ```c
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • ```
  • Here the token concatenation operator `##` was used, which wouldn't be possible if we had items in the list consisting of more than one pre-processor token, for example `char*`. Nor would it be possible if the list contained items which can't exist in a C identifier, `print_char*` would be invalid syntax.
  • Next up suppose we have a list of data:
  • ```c
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • ```
  • And we wish to print this with a generic function interface `print()`, no matter which data we put in, to call the various type-specific functions defined earlier. Such a function call could be made type safe with for example:
  • #define print(val) _Generic(val, int: print_int) (val)
  • And then just add `double: print_double` etc for each supported type. But typing that manually is _too easy_ :) And note generic enough - why not use our supported types X macro for the generic association list itself:
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • Note that the `fmt` parameter of the X macro was completely ignored, which is something we can always chose to do. Also in the specific case of `_Generic`, it doesn't support trailing comma, hence the peculiar syntax with `,` first.
  • And finally, to completely soak ourselves in X macros, lets call that generic `print` macro for each item in our data list:
  • ```c
  • #include <stdio.h>
  • #define SUPPORTED_TYPES(X) \
  • X(int, %d) \
  • X(double, %f) \
  • X(char, %c) \
  • #define GENERATE_FUNCTIONS(type, fmt) \
  • void print_##type (type param) \
  • { \
  • printf(#fmt "\n", param); \
  • }
  • SUPPORTED_TYPES(GENERATE_FUNCTIONS)
  • #define GENERIC_LIST(type, fmt) ,type: print_##type
  • #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)
  • #define DATA_LIST(X) \
  • X(123) \
  • X(3.1415) \
  • X((char)'A') \
  • int main()
  • {
  • #define PRINT_LIST(val) print(val);
  • DATA_LIST(PRINT_LIST)
  • }
  • ```
  • https://godbolt.org/z/cq7YarM66
  • Preprocessed code (re-formatted here for readability):
  • ```c
  • void print_int (int param)
  • {
  • printf("%d" "\n", param);
  • }
  • void print_double (double param)
  • {
  • printf("%f" "\n", param);
  • }
  • void print_char (char param)
  • {
  • printf("%c" "\n", param);
  • }
  • int main()
  • {
  • _Generic((123),
  • int: print_int,
  • double: print_double,
  • char: print_char) (123);
  • _Generic((3.1415),
  • int: print_int,
  • double: print_double,
  • char: print_char) (3.1415);
  • _Generic(((char)'A'),
  • int: print_int,
  • double: print_double,
  • char: print_char) ((char)'A');
  • }
  • ```
  • ---
  • **Advantages of X macros:**
  • - Very generic and powerful. Data can be turned into identifiers and/or strings as needed.
  • - Reduces code repetition drastically.
  • - Ideal for reducing code maintenance to a single place in the code base.
  • - Ideal for maintaining some old code base where someone already made a fine mess and we need to bring order to it.
  • - A de facto industry standard way of writing macros, instead of cooking up some home-brewed, project-specific macro solutions (very bad practice).
  • **Disadvantages of X macros:**
  • - Hard to read, especially for those not used to seeing them.
  • - Hard to trouble-shoot while writing them.
  • - Although easy to maintain on the code side, they are hard to debug in a debugger - similar to attempting to debugging for example inlined functions.
#1: Initial revision by user avatar Lundin‭ · 2025-02-25T15:25:16Z (3 days ago)
**Purpose and use**

X macros is a design pattern used for the purpose of centralizing data & code maintenance to a single point in the program. Instead of maintaining code based on some data set in multiple places, we can gather the data at one single place, in a macro. And it's all done at compile-time, so there's no run-time overhead.

The idea is that all data, for which we need to do a certain thing with repeatedly, is gathered in a list, the "X macro list". Each item is placed inside a parenthesized, comma-separated expression with all data that belongs together:

```c
#define ANIMAL_LIST                \
  /*animal   legs  sound */        \
  X(cat,     4,    meow)           \
  X(dog,     4,    woof)           \
  X(rooster, 2,    cockadoodledoo) \
```

Each item inside the comma-separated list should preferrably be a valid pre-processor token. We _could_ write each such item as multiple pre-processor tokens too, but that will restrict the flexibility somewhat.

Each X in this list will boil down to a function-like macro call. And so when we use `ANIMAL_LIST` there will be 3 macro calls immediately after each other. Since `ANIMAL_LIST` is one big macro we have to use `\` to separate the lines into something readable for humans.

In the first example from the question and from Wikipedia, they use the style of `#define X(...) something`, then call the macro `X` over and over. Then finally `#undef X` to make the macro name `X` usable again. Example:

```c
#define X(animal, legs, sound) \
  printf("A %s has %d legs and says '%s'.\n", #animal, legs, #sound);

  ANIMAL_LIST  /* note the lack of semicolon here */
#undef X
```

Note that since this is still macros, we may use the `#` "stringification operator" to convert something to a string whenever we want. Or use `##` to concatenate symbols. The above example will result in a pre-processor output like:

```c
printf("A %s has %d legs and says '%s'.\n", "cat", 4, "meow"); 
printf("A %s has %d legs and says '%s'.\n", "dog", 4, "woof"); 
printf("A %s has %d legs and says '%s'.\n", "rooster", 2, "cockadoodledoo");
```

So we may use this for iterating across data, for generating enums, even for generating code.

---

**Preferred style**

I strongly recommend a different style than the `#define X` / `#undef X` one though. As noted, the  `#define X ... #undef X` adds a bit of extra clutter. But it also looks kind of alien to type out a macro name `ANIMAL_LIST` without any parenthesis or semicolon.

There is a different style that removes that clutter while at the same time making the X macros even more flexible. In the preferred style we pass the macro that should be called for each data item as a parameter to the list macro itself:

     #define ANIMAL_LIST(X)

Now that's a bit confusing but more powerful than the previous. On the caller side we wouldn't use the generic identifier X any longer, but instead define a named macro:

    #define ANIMAL_PRINT(animal, legs, sound) ...

And then call the list as

    ANIMAL_LIST(ANIMAL_PRINT)

This gives less clutter and more self-documenting names. But now we may not just call a list of data with different macros - we may also use the macro for different sets of identical data. 

    MAMMAL_LIST(ANIMAL_PRINT)
    BIRD_LIST(ANIMAL_PRINT)

An example where two data lists are used for printing of lots of diverse data:

```c
#include <stdio.h>
#include <stdint.h>

#define DATA_LIST1(X)      \
  X(int,    123,      %d)  \
  X(double, 3.1415,   %f)  \
  X(char*,  "hello",  %s)  \

#define DATA_LIST2(X)      \
  X(char,   'A',      %c)  \
  X(size_t, SIZE_MAX, %zu) \

#define DATA_PRINT(type, val, fmt) printf("%s: " #fmt "\n", #type, val);

int main() 
{
  DATA_LIST1(DATA_PRINT)
  DATA_LIST2(DATA_PRINT)
}
```

---

**Advanced example**

Here is another advanced example mostly just to demonstrate how ridiculously flexible these can get. (If you don't understand the following code then don't worry! It is using a lot of different C tricks all at once.)

Lets define one X macro list for all types we want to support and their respective `printf` format specifiers:

```c
#define SUPPORTED_TYPES(X) \
  X(int,    %d)            \
  X(double, %f)            \
  X(char,   %c)            \
```

Then use this to generate code for 3 different functions, `print_int`, `print_double` and so on:

```c
#define GENERATE_FUNCTIONS(type, fmt) \
  void print_##type (type param)      \
  {                                   \
    printf(#fmt "\n", param);         \
  }
SUPPORTED_TYPES(GENERATE_FUNCTIONS)
```

Here the token concatenation operator `##` was used, which wouldn't be possible if we had items in the list consisting of more than one pre-processor token, for example `char*`. Nor would it be possible if the list contained items which can't exist in a C identifier, `print_char*` would be invalid syntax.

Next up suppose we have a list of data:

```c
#define DATA_LIST(X)       \
  X(123)                   \
  X(3.1415)                \
  X((char)'A')             \
```

And we wish to print this with a generic function interface `print()`, no matter which data we put in, to call the various type-specific functions defined earlier. Such a function call could be made type safe with for example: 

    #define print(val) _Generic(val, int: print_int) (val)

And then just add `double: print_double` etc for each supported type. But typing that manually is _too easy_ :) And note generic enough - why not use our supported types X macro for the generic association list itself:

    #define GENERIC_LIST(type, fmt) ,type: print_##type
    #define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)

Note that the `fmt` parameter of the X macro was completely ignored, which is something we can always chose to do. Also in the specific case of `_Generic`, it doesn't support trailing comma, hence the peculiar syntax with `,` first.

And finally, to completely soak ourselves in X macros, lets call that generic `print` macro for each item in our data list:

```c
#include <stdio.h>

#define SUPPORTED_TYPES(X) \
  X(int,    %d)            \
  X(double, %f)            \
  X(char,   %c)            \

#define GENERATE_FUNCTIONS(type, fmt) \
  void print_##type (type param)      \
  {                                   \
    printf(#fmt "\n", param);         \
  }
SUPPORTED_TYPES(GENERATE_FUNCTIONS)

#define GENERIC_LIST(type, fmt) ,type: print_##type
#define print(val) _Generic((val) SUPPORTED_TYPES(GENERIC_LIST)) (val)

#define DATA_LIST(X)       \
  X(123)                   \
  X(3.1415)                \
  X((char)'A')             \

int main() 
{
  #define PRINT_LIST(val) print(val);  
  DATA_LIST(PRINT_LIST)
}
```

https://godbolt.org/z/cq7YarM66

Preprocessed code (re-formatted here for readability):

```c
void print_int (int param) 
{ 
  printf("%d" "\n", param); 
} 

void print_double (double param) 
{ 
  printf("%f" "\n", param); 
} 

void print_char (char param) 
{ 
  printf("%c" "\n", param); 
}

int main()
{
  _Generic((123),
           int:    print_int,
           double: print_double,
           char:   print_char) (123); 

  _Generic((3.1415),
           int:    print_int,
           double: print_double,
           char:   print_char) (3.1415); 

  _Generic(((char)'A'),
           int:    print_int,
           double: print_double,
           char:   print_char) ((char)'A');
}
```

---

Advantages of X macros:
- Very generic and powerful. Data can be turned into identifiers and/or strings as needed.
- Reduces code repetition drastically.
- Ideal for reducing code maintenance to a single place in the code base.
- Ideal for maintaining some old code base where someone already made a fine mess and we need to bring order to it.
- A de facto industry standard way of writing macros, instead of cooking up some home-brewed, project-specific macro solutions (very bad practice).

Disadvantages of X macros:
- Hard to read, especially for those not used to seeing them.
- Hard to trouble-shoot while writing them.
- Although easy to maintain on the code side, they are hard to debug in a debugger - similar to attempting to debugging for example inlined functions.