Hacker News new | ask | show | jobs
by wahern 3666 days ago
_Generic absolutely can handle uint8_t. In fact, the reason _Generic is problematic is precisely because on most implementations uint8_t is a typedef to unsigned char. But in a _Generic list you can't specify multiple compatible types. If you're unsure if uint8_t is compatible with unsigned char, you have to chain multiple _Generic expressions, nesting one inside the default: case like: `_Generic(x, uint8_t: foo_u8, default: _Generic(x, unsigned char: foo_uc, default: baz))`.

So if I specify both unsigned char and uint8_t as cases the same _Generic expression, with GCC 6.1 I get:

  foo.c:6:2: error: ‘_Generic’ specifies two compatible types
    uint8_t: "uint8_t", \
    ^
  ...
  foo.c:5:2: note: compatible type is here
    unsigned char: "unsigned char", \
    ^
and with Apple clang-7001.81 I get

  foo.c:12:22: error: type 'uint8_t' (aka 'unsigned char') in generic association
        compatible with previously specified type 'unsigned char'
Another issue with _Generic: you have to be careful with type promotion, especially because everything smaller than int is quickly promoted to int in most kinds of expressions.

Another issue is type qualifiers: (int) is different from (const int) is different from (volatile int) is different from (const volatile int). _Atomic and restrict increase the permutations.

I have a fuzzy memory that early clang had a wrong implementation of _Generic that didn't obey the standard. But as far as I know, today both clang and GCC have identical behavior. Whether Microsoft implements it compatibly if they add it is another question. For example, Microsoft has an idiosyncratic interpretation of macro tokenization and evaluation that makes implementing certain C99 variable argument macro constructions difficult.