Live data from Hacker News

Workarounds for C11 _Generic()

chiark.greenend.org.uk

51–58 of 58 posts

Re: Workarounds for C11 _Generic()

#51

I would have designed the feature like this: _Generic( , type1 : ( expr1 ), type2 : ( expr2 ), ... default : ( expr )) Here, the parentheses shown in this phrase pattern are required. The implementation would only parse and semantically analyze the expression of the matching type. For the others, the ( expr ) would be treated as a token sequence to be skipped, which has to contain valid tokens, and balancing parenthe…

Just admit that it's pattern matching, and introduce a new variable for each match: ``` _Generic(expr0, type1 id1?: expr1, ... typeN idN?: exprN, default id0?: exprDefault ) ``` Every identifier is optional, but if you include `idX`, it gets bound to the value of `expr0` with type `typeX` in the following expression, or with the type of `expr0` for the default case.

Then you can safely refer to the value at its now-known type, even if `expr0` was not a variable to begin with.

Re: Workarounds for C11 _Generic()

#52
post #49

Earlier quoted context omitted.

> 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.

Indeed. 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.

By 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.

Re: Workarounds for C11 _Generic()

#53
post #49

Earlier quoted context omitted.

Indeed. 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.

By 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 synta…

> 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.

Re: Workarounds for C11 _Generic()

#54
post #49

Earlier quoted context omitted.

> 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.

Indeed. 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.

You say "indeed" but you don't seem to understand my point: Nobody is asking for a fancy type system. If anything, they're asking for less type system in this specific situation.

But really it's just a matter of which type it uses for the expression, where neither option is fancier than the other.

The type system does not need a complete rework to support this.

Re: Workarounds for C11 _Generic()

#55
post #53

Earlier quoted context omitted.

By 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 synta…

> 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.

> 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.

Re: Workarounds for C11 _Generic()

#56

Earlier quoted context omitted.

> Macro replacements occur prior to actual compilation, so no compiler context exists for x, as such all code paths must be valid. How is it throwing an error like "invalid type argument of ‘->’" if there's no compiler context? And why does an error like that get in the way of macro replacements? (That might be the wrong message but the article says you at least get an equivalent.)

I mean in the sense of expanding the macro. It doesn't look at the type of operand at the macro expansion step, it simply replaces it with the _Generic() function call, which contains all the expressions, and each expression is parsed, and thus must be valid.

> It doesn't look at the type

> each expression is parsed, and thus must be valid.

There's multiple kinds of "valid".

The code has valid syntax but invalid types.

The preprocessor doesn't understand types, so it can't know the code has invalid types, right? So this error is not happening in the preprocessor, right?

Therefore changing this behavior would not require preprocessor changes/shoehorning. This error is not a consequence of how the preprocessor works.

Re: Workarounds for C11 _Generic()

#57
post #21

Earlier quoted context omitted.

You make it sound alot more complicated. Ignoring the library functions or stuff you can include from safe coding standard headers. You just do something like. BOOL AddOverflowUnsigned(BYTE* a, BYTE* b, INT32 sizea, INT32 sizeb) { BOOL IsMsb = PlatformIsMSB(); BOOL IsLsb = PlatformIsLSB(); //log error if(!IsMsb && !IsLsb) return FALSE; //Early out for overflow. Assuming if(sizea == sizeb && PlatformIsMSB()) { BOOL AM…

First of all, you forgot to return the actual sum from the function. Second, original add_overflow-style functions support arbitrary expressions as input arguments, not just pointers to l-values. And third, there is no way all this stuff is going to get optimized down to inlined add rdi, rsi setc eax mov [rdx], rdi

Return value is returned in a, it returns true or false on whether overflow happened. And you're assuming that you're on x86_64. You can't do that in C. C is platform independent. Which is why I said you can use one of the functions or intrinsics, and writing it yourself is bad, but still easy.

Re: Workarounds for C11 _Generic()

#58
post #21

Earlier quoted context omitted.

You make it sound alot more complicated. Ignoring the library functions or stuff you can include from safe coding standard headers. You just do something like. BOOL AddOverflowUnsigned(BYTE* a, BYTE* b, INT32 sizea, INT32 sizeb) { BOOL IsMsb = PlatformIsMSB(); BOOL IsLsb = PlatformIsLSB(); //log error if(!IsMsb && !IsLsb) return FALSE; //Early out for overflow. Assuming if(sizea == sizeb && PlatformIsMSB()) { BOOL AM…

This is OK as a fallback, even if it's not at all strictly compliant (the standard still allows PDP or Honeywell endian and padding bits wherever). As the main option it's extremely silly, though, when the entirety of the function on e.g. x86-64 could be (using the GCC signature but MS calling convention and syntax for according to your apparent preference) ; bool add_overflowll(long long x, long long y, long long *r…

That relies on architecture things that arn't in C. and that's why I say "ignoring the library functions or stuff you can include from safe coding standard headers. ". Of course you can do an intrinsic or whatever, and that's better obviously. But I wanted to show how easy it is to write a platform independent function to do this from raw C.
Post reply on HN