liveBook · Manning

Type-generic interface

As explained in Modern C, Third Edition

Type-generic programming is a programming technique that selects or adapts operations according to the types of values involved.

A type-generic interface gives one operation name to behavior that depends on an argument’s type. For example, <tgmath.h> provides a type-generic cos macro instead of requiring callers to choose among cos, cosf, and cosl for double, float, and long double. Each such macro represents a finite set of functions. section 18.1.7

#define fabs(X)       \
_Generic((X),         \
  float: fabsf,       \
  long double: fabsl, \
  default: fabs)(X)

A _Generic expression selects a function according to the type of X; the selected function is then called with X. section 18.2

_Generic is a C11 expression, not itself a macro facility. Its controlling expression guides a choice among type associations and an optional default. Placing that choice in a function-like macro supplies a single call interface: in the fabs example, float selects fabsf, long double selects fabsl, and other arithmetic types fall through to fabs. section 18.2

TAKEAWAY: choose the mechanism—and check its limits—before treating one name as a universal operation.

  • Arithmetic expressions in macros can use the usual promotions and conversions to work across types. Macros can also generate declarations or statement-like operations, but statement macros need care with argument evaluation and syntax. section 18.1.3
  • _Generic selects from specified types. Interfaces involving combinations of types require more cases and attention to conversions; a minimum-of-two-values macro can use the sum of its arguments to select among floating-point functions. section 18.0 section 18.2
  • Type inference with C23 auto, typeof, and typeof_unqual offers another way to maintain type consistency without enumerating every case. section 18.0 section 18.5
#include "c23-fallback.h"

char const unmut_str[] = "haystack";
char         mut_str[] = "hoystack";
char const    needle[] = "stack";
char*         mut_pos0 = strstr(unmut_str, needle);   // error
char const* unmut_pos0 = strstr(unmut_str, needle);   // ok
char*         mut_pos1 = strstr(mut_str, needle);     // ok
char const* unmut_pos1 = strstr(mut_str, needle);     // ok
char*         mut_pos2 = (strstr)(unmut_str, needle); // possible invalid access

The C23 strstr macro interface preserves a const-qualified argument’s qualification in the returned pointer. Parenthesizing the name calls the function interface instead, bypassing that protection. section 18.1.7

Type-generic interfaces extend beyond math: C23 adds qualification-preserving interfaces for selected string-search and bsearch functions, while <stdatomic.h> has interfaces for atomic types. Their implementations need not use the same selection technique. section 18.1.7

How Modern C, Third Edition covers it

How deep it goes
Explains macro and _Generic selection with examples, including fixed-type limits and qualification safety; does not detail atomic-interface implementations.
Examples
Uses cos and fabs function families, a two-argument minimum macro, and const-qualified strstr calls.
What it assumes
Assumes familiarity with C operators, conversions, and macros; introduces explicit _Generic selection from its syntax.
What you can do after
Build it from scratch with a _Generic macro that selects functions for a specified set of argument types.
How it connects
Connects macros and _Generic to arithmetic conversions, function pointers, const qualification, and type inference.

Also explained in

Compare how 2 books explain type-generic interface →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Modern C, Third Edition. The section links point to the parts of the book it draws on.