C11 Generic Selection: Purpose and Use of _Generic
This article provides an overview of the generic selection feature
introduced via the _Generic keyword in the C11 standard. It
explores the core purpose of generic selection, explains how it enables
compile-time type dispatching in standard C, and outlines how developers
use it to implement function overloading, build type-generic math
interfaces, and write safer, more maintainable code without runtime
overhead.
The Core Purpose of Generic Selection
Before the C11 standard, C lacked native support for function
overloading. If a programmer needed a function that operated on multiple
data types—such as float, double, and
int—they had to write distinctly named functions for each
type (for example, abs(), labs(), and
fabs()).
The primary purpose of the _Generic keyword in C11 is to
provide compile-time type-based expression selection. It allows C
programmers to associate different expressions with specific data types,
effectively mimicking function overloading and type genericity while
preserving the simple ABI and procedural nature of the language.
Syntax and Mechanics
Generic selection operates purely at compile time. The basic syntax is structured like a compile-time switch statement based on types:
_Generic( controlling-expression, \
type1: result1, \
type2: result2, \
default: default_result \
)- Controlling Expression: The expression whose type is inspected. Crucially, this expression is unevaluated at runtime; the compiler only determines its resulting type.
- Type Associations: A list mapping data types to specific replacement expressions or function pointers.
- Default Case: An optional fallback expression selected when the controlling expression's type matches none of the listed types.
During compilation, the compiler inspects the type of the controlling expression, selects the matching branch, and discards all other branches.
Common Use Cases
1. Pseudo-Function Overloading
The most frequent application of _Generic is pairing it
with the preprocessor to create function wrappers that automatically
route arguments to the correct implementation.
#include <stdio.h>
void print_int(int x) { printf("Integer: %d\n", x); }
void print_double(double x) { printf("Double: %f\n", x); }
void print_string(char *x) { printf("String: %s\n", x); }
#define print(X) _Generic((X), \
int: print_int, \
double: print_double, \
char*: print_string \
)(X)Calling print(42) routes to print_int(42),
while print(3.14) routes to
print_double(3.14).
2. Standardized
Type-Generic Math (<tgmath.h>)
Historically, the type-generic math library
(<tgmath.h>) introduced in C99 relied on
compiler-specific built-ins and magic macros. C11 standardizes this
behavior using _Generic, allowing standard functions like
sin() to resolve to sinf(),
sin(), or sinl() depending on whether the
argument is a float, double, or
long double.
3. Type Assertions and Safety
Generic selection can enforce compile-time validation. If an
unexpected type is passed and no default branch is
provided, the compiler issues an error, preventing silent conversions
and type-mismatch bugs.
Advantages Over Pre-C11 Alternatives
- Zero Runtime Overhead: Because generic selection resolves during compilation, it adds no CPU cycles, stack frames, or memory overhead to the running program.
- Preserved C ABI: Unlike C++ name mangling, C functions remain simple symbols, retaining full compatibility with dynamic linkers and foreign function interfaces (FFIs).
- Cleaner APIs: Library authors can expose unified, intuitive interfaces to users without requiring them to remember distinct names for every numerical variant of an algorithm.