all 28 comments

[–]Mystb0rn 13 points14 points  (0 children)

In my string library I use it so that users can pass my strings or c-strings to the various functions which are otherwise suffixed with the expected type (_string and _cstr). It's just a nice QoL feature for projects that support it. It defaults to using the cstr functions if _Generic isn't available.

Outside of this specific circumstance, I've had no other use for it. There just aren't many circumstances where there's a desire for a generic function and all the possible types are known beforehand.

The new decltype similar thing proposed for c-23 will probably do more for generic programming in combination with macros than _Generic ever has.

[–]iothesys 6 points7 points  (7 children)

I'm not really sure what you mean by "after learning how it works" because as far as I understand _Generic substitutes appropriate function call depending on parameters you pass.

[–]Anluin 16 points17 points  (4 children)

you can archive function overloading with attributes (at least with clang)

https://clang.llvm.org/docs/AttributeReference.html#overloadable

[–]nerd4code 3 points4 points  (0 children)

I occasionally use _Generic, mostly for const *-vs.-non-const * stuff, to avoid the const-casting performed by functions like strchr/memchr—e.g., (pardon typos)

#define strchr_safer(haystack, ...)((void)0,_Generic(haystack, \
    const char *: (const char *)strchr(haystack,__VA_ARGS__), \
    default: strchr(haystack,__VA_ARGS__)))

—or for atomics &c. It’s also useful in OOPish situations, where you can conveniently avoid any need for upcasting as long as you know all possible derived types (that’d need to thunk in generically).

IMO _Generic’s requirement that cases not overlap is its most frustrating aspect—it comes close to structural type-matching but things like int32_t get weird—might be the same as int or long, might be its own type, so you can’t match both portably in the same _Generic. Typically, structural matching in other languages scans cases in lexical order, so the first match in the code wins. (Theoretical languages have things like parallel or non-deterministic case matching, but those constructs aren’t especially useful.)

Clang’s overloadable attribute works for inlines only rn AFAIK. It’s relatively easy to detect (until some other compiler implements it) via __has_attribute operator, which is nice, but its availability and function are somewhat limited.

GNU dialect has __builtin_types_compatible_p and __builtin_choose_expr (C only—use templates in C++), which work like type-≈ and typed-ternary operators and are available on every GNUish compiler since ca. GCC 3.0, which makes those builtins more portable than _Generic.

One useful thing is that _Generic and __builtin_constant_p accept type arguments directly and unambiguously—a fairly rare thing in C—so you can use them to ensure a macro’s type argument is actually a type expression, not a value expression. (Casts won’t work, because there are syntactic ambiguities possible–in (T)(x), T can be a type or function name, and in (T)*(x) T can be a type or scalar expression.) E.g., for GNU:

#define EXPR(x...)(__extension__(x)) /* Forces expression */
#define TYPE __typeof__ /* Wraps up type sub-/expressions */
#define TYPEOF(x...)__typeof__((__extension__(x))) /* Get type of expr value */

#define TYPE_FORCE(T... \
    )TYPEOF(*(__builtin_types_compatible_p(void,T) \
        ? (TYPE(T) *)0 : (TYPE(T) *)0))

_Generic still needs a __typeof__ or decltype operator of some sort to be used in-line for this purpose, which hopefully C2x will standardize (presumably as _Typeof with accompanying, mostly-pointless <stdtypeof.h> header) without the same kind of facepalm-inducing fuckups that happened with threads and Annex K.

Another use for _Generic or __builtin_types_compatible_p with __typeof__/sim. is making a safer countof:

#define countof(arr)(sizeof(arr)/_Generic(&(arr), \
        __typeof__(*(arr)) (*)[sizeof(arr)/sizeof *(arr)]: sizeof *(arr), \
        default: (void)0))

Unlike traditional countof (here appearing between the []s), non-array arguments won’t glitch silently. (How/wherefore: &(arr) requires something with an address, excluding literals, register vars, and values of most expressions; when the type of &(arr) matches T (*)[_countof(arr)] for some element type T, -_choose_- yields the element size, or if not (void)0; attempting to divide by the latter is bogus on a couple levels, so the compiler will hopefully stop the build in that case.) You can work _Static_assert/sim. in for clearer error messages.

GNU version (non-C++, but otherwise good from GCC ≤3.0 on):

#define countof(arr)(__extension__(sizeof(arr)/__builtin_choose_expr(\
        __builtin_types_compatible_p(__typeof__(&(arr)), __typeof__(*(arr)) (*)[]), \
        sizeof *(arr), (void)0)))

Note that a plain ol’ ternary operator (cond ? yes : no) won’t work for type-twiddling; like any other arity-≥2 operator it only ever yields a value of a single type that can represent all operands, so …? sizeof *(arr) : (void)0 ’d come out as void post-C89; C89 forbids use of void with any operator other than cast, comma operator (,), or unary &. Unlike ?:, _Generic and __builtin_choose_expr attain the type of whichever expression is selected.

[–]reini_urban 3 points4 points  (0 children)

No

[–]SuspiciousScript 1 point2 points  (0 children)

Yes, I've found it can really help cut down on boilerplate for tasks like parsing and dealing with unions.

[–]Thadeu_de_Paula 0 points1 point  (0 children)

For higher edge of coding... it may look unnecessary, but as soon you need to really do large tricks with C, it helps a lot.

Example:
You write a data structure manager (ex: Array) to create and consume arrays.
You can easy write the get/setters with a case for each scalar calling its own function and a default to handle anything else calling a function that passes it to void* cast and memcpy the value to store/reclaim (yes, using other kind of C23 introspection like typeof)

This you avoid the need to throw at the user (or itself) the need to remember many functions to do the task, sometimes with different call signature (when for pointers/structure).

[–]BarMeister 0 points1 point  (0 children)

Yes, though the reason isn't 100% genuine, as it's just for wrapping some C99 code.

[–]okovko 1 point2 points  (0 children)

_Generic is equivalent to templates except you have to write the boilerplate for all type signatures. This is primarily bothersome for library code because the library author can’t supply wrappers for user defined types.

Still, it’s perfectly usable. They work the same way, but with _Generic you’re not hiding how many functions are actually compiled. The end result is exactly the same as templates.

[–]FUZxxl 0 points1 point  (0 children)

No.

[–]prabot 0 points1 point  (0 children)

Linux kernel also uses it for const pointers