Maybe C just shouldn't include generics. Especially not as part of the macro layer.
Maybe C just shouldn't include generics. Especially not as part of the macro layer.
If you have a set of functions for addition with overflow detection, say add_overflow{i,l,ll}, and you have a pair of ptrdiff_t’s or int32_t’s or whatnot that you know are standard integer types, and you want to use the appropriate add_overflow* function, can you do it?
With _Generic you can. Without it I think you’re stuck providing separate functions for every integer typedef in the standard library and then requiring all library authors to do the same for both integer typedefs and integer-accepting functions that they define.
(_Generic is not part of the macro level, that’s why the semantic-checking issues discussed in the article even arise. The C preprocessor can still be implemented as a separate binary that doesn’t understand C itself, even in C23.)
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 AMsb = (a >> ((sizea * PlatformByteSize()) - 1)) & 1;
BOOL BMsb = (b >> ((sizeb * PlatformByteSize()) - 1)) & 1;
if(AMSB && BMsb)
{
//We overflow, early out.
return FALSE;
}
else if(sizea == sizeb && PlatformIsLSB())
//Code here.
//We add using bitwise operators so we can do it on any size.
//SUM = A XOR B XOR CARRY, return in A
//Impliment algo here.
return TRUE;
}
int main()
{
UINT64 a = 69;
UINT64 b = 420;
if(sizeof(a) !
return AddOverflowUnsinged(&a, &b, sizeof(a), sizeof(b));
}
But they have this in the STD now and also all the secure coding libs have this in it's header as a basic function. There's also the easier implementation of just adding a check to see if it will overflow, rather than add in the function.You also only have a few you can hardcode if you don't do it generic, since all will go back to the primitives of uint8, uint16 ....
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 *result)
add_overflowll PROC
add rcx, rdx
seto eax
mov qword ptr [r8], rcx
ret
add_overflowll ENDP
for a total of no loops, no conditional branches, and three instructions with no branches at all when inlined (not shown because I don't remember the MS inline assembly syntax). Why check beforehand when the CPU does the check for you on every arithmetic operation and all you need to do is to ask it for the result? (Unless your CPU is RISC-V because RISC-V.)And of course the newfangled standard functions are size-dependent (more like mine than yours), so either you still need _Generic or essentially equivalent compiler-specific magic, or you have to introduce an ABI dependency on which integer type your ptrdiff_t or ssize_t or off_t or whatever typedef you got from a random place in a library or OS header actually ends up being.
add rdi, rsi
setc eax
mov [rdx], rdiThe behavior with _Generic is basically emergent behavior from implementation of macro processors. Macro replacements occur prior to actual compilation, so no compiler context exists for x, as such all code paths must be valid.
Its much easier to require the programmer typecast x in the replacement value then start trying to shoehorn the compiler context into macro processors.
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.)
> 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.
Without it, we can parse the preprocessed source code.
With the enhancement proposed, we need to do half the compiler's work.
In tools like IDE, we want quick (sub millisecond) feedback for most edits