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 \
)
  1. Controlling Expression: The expression whose type is inspected. Crucially, this expression is unevaluated at runtime; the compiler only determines its resulting type.
  2. Type Associations: A list mapping data types to specific replacement expressions or function pointers.
  3. 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.