Choosing a Type‑Generic abs Implementation in C11 with _Generic Macros
Guide to choosing a type‑generic abs implementation in C11 using _Generic macros and inline functions, with trade‑offs, sample code, and verification steps.
22 Aug 2025, 05:15 UTC

Decision and constraints
You need a single abs name that works for int, long, float and double without runtime overhead, while preserving strict type checking and requiring only a C11‑conforming compiler.
Comparison of supported options
| Option | Dispatch mechanism | Compile‑time overhead | Type safety | Debugging ease |
|---|---|---|---|---|
| A. _Generic → inline functions | _Generic selects an inline function at compile time | None (inlined) | Full – each type calls its own typed function | Easy – symbols appear in debugger |
| B. Macro‑only switch (sizeof/_Generic) | Macro expands to a chain of ternary expressions | Possible multiple evaluations of the argument | Partial – macro may hide type mismatches | Harder – macro expansion obscures source location |
| C. void* generic function | Function receives void* and casts inside | Function call overhead unless inlined | Lost – compiler cannot check argument type | Medium – generic function appears, but casts hide errors |
Trade‑offs
- _Generic + inline gives zero‑overhead calls and full diagnostics, but requires a compiler that implements C11
_Generic(GCC 4.9+, Clang 3.5+, MSVC 2015+). - Macro‑only works in C99, yet the argument may be evaluated more than once and error messages are less precise.
- void* approach avoids the need for
_Genericbut sacrifices type safety and may inhibit optimisations because the compiler sees only a generic pointer.
Implementation using _Generic and inline functions
- Define a type‑specific inline absolute‑value function for each supported type.
- Create a
_Genericmacro that maps an expression to the appropriate inline function. - Invoke the macro as if it were a regular function.
/* abs_generic.h */
#ifndef ABS_GENERIC_H
#define ABS_GENERIC_H
static inline int abs_int(int x) { return x < 0 ? -x : x; }
static inline long abs_long(long x) { return x < 0 ? -x : x; }
static inline float abs_fabsf(float x) { return x < 0.0f ? -x : x; }
static inline double abs_fabs(double x) { return x < 0.0 ? -x : x; }
#define abs(x) _Generic((x), \
int: abs_int, \
long: abs_long, \
float: abs_fabsf, \
double: abs_fabs)(x)
#endif /* ABS_GENERIC_H */
Concrete usage and validation
Create a small test program that prints the absolute value for each type.
/* test_abs.c */
#include
#include "abs_generic.h"
int main(void)
{
int i = -42;
long l = -123456L;
float f = -3.14f;
double d = -2.71828;
printf("abs(int) = %d\n", abs(i));
printf("abs(long) = %ld\n", abs(l));
printf("abs(float) = %f\n", abs(f));
printf("abs(double) = %f\n", abs(d));
return 0;
}
Build and run (GCC/Clang)
gcc -std=c11 -Wall -Wextra -O2 test_abs.c -o test_abs
./test_abs
Expected output (values may vary with input):
abs(int) = 42
abs(long) = 123456
abs(float) = 3.140000
abs(double) = 2.718280
Assembly check (zero‑overhead)
gcc -std=c11 -O2 -S test_abs.c -o -
Look for calls directly to the inline functions (e.g., abs_int, abs_fabsf) with no intermediate dispatch code.
Verifying C11 dependency
gcc -std=c99 -Wall -Wextra test_abs.c -o test_abs_c99 2>&1
The compilation should fail with an error similar to “error: '_Generic' not supported in ISO C90/ISO C99”.
Limitations and practical checks
- The macro only works for the types explicitly listed; passing an unsupported type (e.g.,
short) yields a compile‑time error, which is desirable for safety but requires updating the macro when new types are added. - If you need a fallback for unsupported types, you can add a default clause using
default:(available since C11) that calls a generic function, but this re‑introduces runtime overhead. - To confirm that the macro expands correctly, run the preprocessor:
gcc -std=c11 -E test_abs.cand verify that eachabs(...)line becomes a direct call to the appropriate inline function.
Summary
For a zero‑overhead, type‑safe abs interface in modern C, the _Generic macro dispatching to inline functions is the preferred solution when you can rely on a C11‑conforming compiler. The table above shows why the alternative macro‑only or void* approaches are less suitable for performance‑critical or safety‑critical code.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.