There are no Generics in C

_Generic is not a generic in C It is manual name mangling + function overloading.

C is not a perfect language. One of the things that I often end up wanting to use is something akin to Generics (from Rust/Java) or Templates (from C++).

“Generics” typically implies a mechanism for code generation a blueprint that allows the compiler to make new functions for new types automatically. C’s _Generic does no such thing. It generates nothing; it merely points to code you have already manually written.

Example

Let’s look at a simple problem. I want a function foo that prints a value. I want to pass it an int, or a float, or a string.

In C, because we lack function overloading, we have to do this:

void foo_int   (int i)            { printf("Int:    %d\n",  i);  }
void foo_float (float f)          { printf("Float:  %f\n",  f);  }
void foo_cstr  (const char *s)    { printf("String: %s\n",  s);  }

int main(void) {
    foo_int(69);
    foo_float(420.0f);
    foo_cstr("Hellow, World!\n");
}

This is annoying. We have to remember the specific function name for every data type.

The C11 Solution

C11 introduced the _Generic macro, it allows us to write a macro that essentially acts as a compile time switch statement based on the type of the argument.

#define foo(x) _Generic((x), \
    int   : foo_int,         \
    float : foo_float        \
    char *: foo_cstr         \
)(x)

int main(void) {
    foo(69);                // Expands to foo_int(69)
    foo(420.0f);            // Expands to foo_float(420.0f)
    foo("Hello, World!\n"); // Expands to foo_cstr("Hello, World!")
}

This looks like function overloading. You call foo with different types, and it works. But let’s look closer at what is actually happening.

Manual Name Mangling

In C++, you can just write void foo(int x) and void foo(float x). The compiler handles the rest. Under the hood, the C++ compiler performs Name Mangling.
It renames your functions to something like _Z3fooi (for int) and _Z3foof (for float) inside the object file so the linker can tell them apart.

In C, _Generic forces you to do the name mangling manually.

In C++, the compiler generates specific function variants for you via Templates or handles the unique naming required for Overloading automatically. In C, however, _Generic forces you to do this legwork. You must write foo_int and foo_float yourself, and then you must manually write the dispatch logic to route the calls correctly.

If you decide to add support for a new type, say a double, the workload increases linearly. You first have to implement a dedicated foo_double function. Then, you must return to your _Generic macro and manually insert a new case mapping the double type to that specific function.

Generics vs. Dispatching

True Generics (like in Rust) or Templates (like in C++) are about Code Generation. When you write fn foo<T>(x: T), the compiler generates a unique version of that function for every T you use.

_Generic in C is about Dispatching. It doesn’t generate code. It doesn’t write functions for you. It just looks at a type list and points to a function you already wrote.

It is a useful feature. It powers tgmath.h (Type-Generic Math), which allows you to call sin(x) without worrying if x is a float or a double but it is: Type-Safe Macro Dispatching, not Generics.