Lynne's compiled musings

21-06-21

Generics in C11

There's a persistent belief that C generics are all useless, shouldn't be called Generics at all and in general should not have been standardized.
I'm not going to debate whether they should have been a part of the language, nor why they're called generics, but they are nonetheless a part of the language because someone thought they could be useful. So here's one useful application: filling in structs and saving you from copying or specifying things the compiler knows about already:

typedef struct SPRational {
    int64_t num;
    int64_t den;
} SPRational;

enum SPDataType {
    SP_DATA_TYPE_UNKNOWN = 0,
    SP_DATA_TYPE_FLOAT,
    SP_DATA_TYPE_DOUBLE,
    SP_DATA_TYPE_INT,
    SP_DATA_TYPE_UINT,
    SP_DATA_TYPE_U16,
    SP_DATA_TYPE_I16,
    SP_DATA_TYPE_I64,
    SP_DATA_TYPE_U64,
    SP_DATA_TYPE_STRING,
    SP_DATA_TYPE_RATIONAL,
};

#define D_TYPE(x) { #x, (&(x)), _Generic((x),                                  \
    float: SP_DATA_TYPE_FLOAT,                                                 \
    double: SP_DATA_TYPE_DOUBLE,                                               \
    int32_t: SP_DATA_TYPE_INT,                                                 \
    uint32_t: SP_DATA_TYPE_UINT,                                               \
    uint16_t: SP_DATA_TYPE_U16,                                                \
    int16_t: SP_DATA_TYPE_I16,                                                 \
    int64_t: SP_DATA_TYPE_I64,                                                 \
    uint64_t: SP_DATA_TYPE_U64,                                                \
    char *: SP_DATA_TYPE_STRING,                                               \
    SPRational: SP_DATA_TYPE_RATIONAL,                                         \
    default: SP_DATA_TYPE_UNKNOWN                                              \
) }

typedef struct SPDataSample {
    const char *name;
    void *location;
    enum SPDataType type;
} SPDataSample;

static int some_function(void)
{
    int some_variable;
    double some_other_variable;
    const char *some_string;
    SPRational some_rational;

    SPDataSample data_rep[] = {
        D_TYPE(some_variable),
        D_TYPE(some_other_variable),
        D_TYPE(some_string),
        D_TYPE(some_rational),
    };
}

The macro simply expands to { "some_variable", &some_variable, SP_DATA_TYPE_INT }. Sure, its almost no work to write it out explicitly like that. But if you don't care about C99/89 compatibility and somehow the idea of using new language features which only a few understand is appealing, go for it.

Of course, this is not the only application of generics, after all the tgmath.h header makes extensive use of them, and they can be useful to auto-template some DSP code. But for that case I'd much rather explicitly write it all out.

 ·  c  ·  CC-BY logo