5 ms·
According to this the "big bug" is that... _Generic works mostly like a macro and expands code that the compiler sees. That seems like a little weak, macros ha
by ajross 3y ago
According to this the "big bug" is that... _Generic works mostly like a macro and expands code that the compiler sees. That seems like a little weak, macros have been doing this forever via a mere extra level of indirection.
So sure, "(x)->length" might not be valid syntax in all configurations the compiler might see. But "LENGTH(x)" is, e.g.:
#if X_MIGHT_BE_MYSTRINGBUFFER
#define LENGTH(x) ((x)->length)
#else
#define LENGTH(x) 0 /* anything that converts to the output type will do */
#endif
This is a routine pattern seen everywhere in C. The Linux kernel is filled with it, e.g. field accessors or arch-specific functions that are stubbed out when not needed, etc...
Is this as clean as a full-on generic typesystem? No. But it's C, it shows a weirdness that you have to handle manually, and you do it the same way we've been doing it in C for decades. Not a new problem, doesn't need a new solution. It's C!
- fanf2 3y agoMacros and #ifdef do not solve the problems that _Generic is used for. _Generic is for static polymorphism, not for target or build configuration.
- ajross 3y ago> Macros and #ifdef do not solve the problems that _Generic is used for. No, but they solve the specific "big bug" described in the article very well. Use _Generic for what it's good for. Use macros to fill in the gaps (in this case build/preprocessor configuration differences that change the syntax used in the RHS of the generic expansion). Again, this is C. You want fancy type systems, you know where to find them. Don't ask for them here, we don't want them.
- Dylan16807 3y ago> You want fancy type systems, you know where to find them. Don't ask for them here, we don't want them. But the problem here is caused by the type system being overly aggressive. If it wasn't trying to force the wrong type onto unused code, everything would work fine.
- ajross 3y agoIndeed. So? Again if you want to code in Haskell or Rust, those are fine choices. What you want, you can't have absent a complete rework of C's type system. And you don't actually want that.
- reichstein 3y agoBy all means work on getting `_Generic` removed again if that is what you want. As long as the feature is there, it should endeavor to be the best possible version of itself. Making it deliberately useless and unsuitable in order to avoid the obvious use of _doing different things for different types_ is really just having a suboptimal feature. And if it would make it a better feature to ignore the parts of the syntax, that are statically known to not be part of the program, it seems like low-hanging fruits. I'm sure there are philosophical reasons for every choice made in the design of this feature, but at the end of the day, users just see a feature being needlessly obtuse.
- ajross 3y ago> Making it deliberately useless How do you get "useless"? I literally showed five lines of code above that solve the putative problem with _Generic by leveraging an idiom we've been using for decades already. Meh. Use what you like. Clearly it's not C.
- Dylan16807 3y ago> I literally showed five lines of code above that solve the putative problem The top reply to your five lines literally points out why your solution doesn't solve the right problem. The macro you wrote puts the same definition everywhere. It can't even handle the original use case of calling a different function for each number type. If you're suggesting a combination approach of hiding the implementation with a macro until after _Generic gets resolved, I don't think that ordering is possible.
- Dylan16807 3y ago
- mostlylurks 3y agoMacros are perfectly capable of static polymorphism. That is one of their primary use cases. _Generic is simply an alternative mechanism for static polymorphism, with some extra capabilities and some limitations in comparison to your typical macro.
- teo_zero 3y agoTo prove your assertion, would you mind defining a length() function via macros that works with both a char * and a pointer to a struct {int len; char *data}, as the fine article explained?
- fanf2 3y agoThere is a mixup here between different flavours of polymorphism. _Generic does ad-hoc polymorphism via type-based dispatch in order to implement function overloading. Macros are more like a limited form of parametric polymorphism, in that they are polymorphic up to what is allowed by the operations they apply to their arguments. In C that typically means relying on implicit or explicit coercions, or C’s mildly overloaded operators.
- kelnos 3y agoI think you've misunderstood the purpose of _Generic. Your #if statement will select exactly one implementation for every single site of the use of LENGTH() in the codebase, depending on the value of X_MIGHT_BE_MYSTRINGBUFFER at compile-time. _Generic() allows you to have both implementations available, and different implementations can be selected at the call site depending on the type of the argument passed.