Using C11 _Generic to Write a Single Type‑Generic Math Macro
C11's _Generic keyword lets you write a single macro that expands to the correct float, double or long double math function, eliminating duplicated code.
10 Mar 2026, 13:14 UTC

Problem: duplicated math macros for each floating‑point type
When you need a cube‑root operation that works with float, double and long double without forcing callers to cast, you often end up with three separate macros or inline functions. Maintaining them is tedious and callers must remember which variant to use.
Thesis: _Generic lets the compiler pick the right function at compile time
C11’s _Generic keyword selects an expression based solely on the type of its controlling expression. By placing the macro argument inside a _Generic list that maps each floating‑point type to the corresponding math library function, the preprocessor expands to the correct call automatically.
Worked example: a type‑generic cbrt macro
First include <math.h> which provides cbrtf, cbrt and cbrtl. Then define the macro:
#include <math.h>
#define CBRT(X) _Generic((X), \
float: cbrtf, \
double: cbrt, \
long double: cbrtl)(X)
Use it with variables or literals:
float f = CBRT(8.0f);
double d = CBRT(27.0);
long double ld = CBRT(125.0L);
Compile with a C11‑compatible compiler (no special privileges needed):
gcc -std=c11 -Wall -Wextra -o cbrt_demo cbrt_demo.c
To verify the expansion, run the preprocessor only:
gcc -E -std=c11 cbrt_demo.c
You should see lines such as cbrtf(8.0f), cbrt(27.0) and cbrtl(125.0L) in the output, confirming that the correct function was selected for each type.
Alternatively, inspect the generated assembly:
gcc -std=c11 -S -o cbrt_demo.s cbrt_demo.c
Look for calls to cbrtf, cbrt and cbrtl respectively.
Trade‑off and limitation
_Generic does not automatically consider const or volatile qualifiers; you must list each qualifier combination explicitly if you need distinct handling. Adding every qualifier quickly makes the macro verbose. If you need to catch unsupported types, add a default clause:
#define CBRT(X) _Generic((X), \
float: cbrtf, \
double: cbrt, \
long double: cbrtl, \
default: (void)(0) /* triggers a compile‑time error if unhandled */)(X)
With a default, the compiler will emit an error like “no matching generic association” when the macro is invoked with, for example, a struct type, making missing cases visible at compile time.
Actionable closing
Replace any duplicated math macros with a _Generic version as shown. Test the macro with the three basic floating‑point types and confirm the correct function appears in the preprocessor output or assembly. If you encounter a missing type, add an association or a default clause to keep the translation unit well‑formed. This gives you compile‑time safety without runtime overhead.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.