Using C11’s _Generic for Type‑Generic Macros
Learn how C11’s _Generic keyword lets you write a single type‑generic macro—illustrated with an abs() example—and understand its trade‑offs and verification steps.
23 Jun 2026, 02:00 UTC

Problem: Repeating Code for Each Numeric Type
When you need a function like absolute value that works for int, long, float and double, the traditional C approach is to write a separate macro or inline function for each type. This leads to duplicated code, a higher chance of inconsistency, and tedious maintenance when the logic must change.
Thesis: _Generic Provides Compile‑Time Type Selection
C11 introduced the _Generic keyword, which lets a single macro choose an expression based on the type of its argument. The selection happens at compile time, so there is no runtime overhead, and the controlling expression is not evaluated—only the chosen branch is.
How _Generic Works
The syntax is:
_Generic( controlling_expression ,
type1: expression1,
type2: expression2,
...
default: expression_default )
The controlling expression is only used to determine its type; it is not executed. The compiler matches the type against the list (including default) and substitutes the corresponding expression. If no match is found and no default is given, the program is ill‑formed.
Worked Example: A Type‑Generic abs() Macro
The following macro expands to the appropriate absolute‑value expression for signed integers, float and double:
#include
#define ABS(x) _Generic((x), \
signed char: ((x) < 0 ? -(x) : (x)), \
short int: ((x) < 0 ? -(x) : (x)), \
int: ((x) < 0 ? -(x) : (x)), \
long int: ((x) < 0 ? -(x) : (x)), \
long long int: ((x) < 0 ? -(x) : (x)), \
float: fabsf(x), \
double: fabs(x), \
default: ((x) < 0 ? -(x) : (x)) /* fallback for unsigned types */
)
To try it, create a file example.c:
#include
#include
#define ABS(x) _Generic((x), \
signed char: ((x) < 0 ? -(x) : (x)), \
short int: ((x) < 0 ? -(x) : (x)), \
int: ((x) < 0 ? -(x) : (x)), \
long int: ((x) < 0 ? -(x) : (x)), \
long long int: ((x) < 0 ? -(x) : (x)), \
float: fabsf(x), \
double: fabs(x), \
default: ((x) < 0 ? -(x) : (x))
)
int main(void) {
int i = -7;
float f = -3.14f;
double d = 2.71;
printf("ABS(%d) = %d\n", i, ABS(i));
printf("ABS(%f) = %f\n", f, ABS(f));
printf("ABS(%lf) = %lf\n", d, ABS(d));
return 0;
}
Compile and run with a C11‑compatible compiler:
- Where to run: any terminal with write access to the current directory.
- Command:
gcc -std=c11 -Wall -Wextra example.c -o example - Permissions: you need read access to
example.cand write/execute permission in the directory to createexample. - Expected check: the program should print the absolute values without warnings.
- Risk: if the macro is used with an expression that has side effects (e.g.,
ABS(get_next())), the side effect occurs only in the selected branch, which is still evaluated—be aware of this when passing complex expressions.
Trade‑offs and Limitations
- Increased macro complexity: the
_Genericlist can become long and hard to read, especially when many types are covered. - Debugging difficulty: stepping through a macro in a debugger shows the expanded line, not the original macro call; you may need to inspect preprocessor output (
gcc -E) to see which branch was chosen. - Compiler requirement: older compilers (pre‑C11) reject
_Genericwith a syntax error. Always compile with-std=c11or later. - Not a function overload: you cannot use
_Genericto overload real functions; it works only within macros or inline functions. - No VLAs: the controlling expression cannot be a variable length array.
Practical Way to Verify the Expansion
To confirm which branch the compiler selected for a given type, examine the preprocessor output:
gcc -std=c11 -E example.c
Look for lines where ABS(...) appears; you will see the concrete expression (e.g., fabsf(-3.140000f)) substituted for each call.
Actionable Closing
If you find yourself writing duplicate macros for different numeric types, try extracting the logic into a _Generic‑based macro as shown. Keep the type list concise, document the fallback, and always test with -std=c11. For cases where debuggability is paramount, consider wrapping the generic logic in a static inline function and using _Generic only to select the appropriate function.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.