Today I came across an interesting concept and wanted to record it. As we all know, C language does not have generics, but C11 introduced a new expression called generic selection expression (_Generic). What does it do? Its function is to select a value based on the type of the expression. Let's take a look at its specific syntax.
Introduction to Generic Selection#
Let's start with a code snippet:
int main()
{
    int x = 1;
    double y = 2.0;
    char z = 'c';
    printf("%d\n", _Generic(x, int:0, double : 1, default:3));
    printf("%d\n", _Generic(y, int:0, double : 1, default:3));
    printf("%d\n",_Generic(z, int:0, double : 1, default:3));
    return 0;
}
_Generic is a C11 keyword. The parentheses following it contain multiple items separated by commas. The first item is an expression, and each subsequent item consists of a type, a colon, and a value, such as double: 1. The value of the entire expression is determined by the type that matches the first item.
The output is as follows:

As you can see, the output varies based on the type of the variable passed in. In the first printf, the first item x is of type int, so the result of the expression is 0. In the second printf, the first item is of type double, so the result is 1. The third printf prints 3 because the type char does not match any label, so the default value is used.
In fact, this is similar to a switch statement, but with _Generic, the labels are matched based on the type of the expression, while with switch, the labels are matched based on the value of the expression.
Combination with Macros#
We can see that the usage above is a bit cumbersome, but we can combine it with macros to make it more convenient. Let's look at an example:
#define MYTYPE(X) _Generic((X),int:"int", double:"double", default:"other")
int main()
{
    int d = 2;
    printf("%s\n", MYTYPE(d));
    printf("%s\n", MYTYPE(1.0*d));
    printf("%s\n", MYTYPE("string"));
    return 0;
}
The output is as follows:

Combining it with macros makes it much easier, and it even feels like C++ generic programming, although it's still not as powerful as C++ generics.
Advanced Usage#
The value corresponding to the _Generic label can be an integer, a string, or even a function pointer. Let's take a look at the following code:
void PrintInt(int x)
{
    printf("%d\n", x);
}
void PrintDouble(double x)
{
    printf("%lf\n", x);
}
void PrintString(char* x)
{
    printf("%s\n", x);
}
void PrintOther(void x)
{
    printf("There is a problem with the type\n");
}
#define PRINT(X) _Generic((X),\
    int:PrintInt,\
    double:PrintDouble,\
    const char*:PrintString,\
    default:PrintOther)(X)
int main()
{   
    int x = 1;
    int y = 2.0;
    const char* str = "hello _Generic";
    PRINT(x);
    PRINT(y);
    PRINT(str);
    return 0;
}
The output is as follows:

Now it looks more like C++ generic programming, right? It's quite interesting, although C++ generics are still more powerful.