[1] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3531.txt
Add a namespacing macro and you have a whole generics system, unlike that in TFA.
So, it might add more value to have the C std add an `#include "file.c" name1=val1 name2=val2` preprocessor syntax where name1, name2 would be on a "stack" and be popped after processing the file. This would let you do types/functions/whatever "generic modules" with manual instantiation which kind of fits with C (manual management of memory, bounds checking, etc.) but preprocessor-assisted "macro scoping" for nested generics. Perhaps an idea to play with in your slimcc fork?
How about making a parameter optional when it isn't a pointer? I don't touch C often, but curiously enough I was just met with this case while making a change in i3:
-Con *con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode);
+Con *con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode, fullscreen_layer_t fullscreen_layer, bool any_layer);
I added that `fullscreen_layer` param, which is an enum, and later found that I needed to make it optional. Maybe there's a better convention for cases like this, but I couldn't think of anything better than adding that `any_layer` boolean to decide whether to use or ignore `fullscreen_layer`. If `fullscreen_layer` were a pointer, it can just be set to `NULL`, but it's not. If the definition allowed without much duplication, I'd split the function into 2, one with the fullscreen_layer param and another without. Again, not an option. I don't want to pollute the enum for the concern of a single function either. If this were Haskell, the convention here would be to wrap the `fullscreen_layer_t` in a Maybe (a parameterized type).Extending on the example of this post to write a Maybe parameterized type in C, it works:
#include <stdio.h>
#define MAYBE(T) struct maybe { bool is_just; T x; }
#define JUST(T, t) ((MAYBE(T)){.is_just = true, .x = (t)})
#define NOTHING(T) ((MAYBE(T)){.is_just = false})
#define FROM_MAYBE(y, m) ((m).is_just ? (m).x : y)
void print_maybe(MAYBE(unsigned) m) {
if (m.is_just) {
printf("%u\n", m.x);
}
}
int main() {
print_maybe(NOTHING(unsigned));
print_maybe(JUST(unsigned, 3));
return 0;
}
I'm not going to use this, because it's kind of unconventional in C and it's just a single case in what I'm working on. However, if this problem were a more common occurrence, something like this might be nice to use to avoid having multiple parameters to pass around when one could be enough. Never mix unsigned and signed operands. Prefer signed. If you need to convert an operand, see (2).
https://nullprogram.com/blog/2024/05/24/You cannot even check the signedness of a signed size to detect an overflow, because signed overflow is undefined!
The remaining argument from what I can tell is that comparisons between signed and unsigned sizes are bug-prone. There is however, a dedicated warning to resolve this instantly.
It makes sense that you should be able to assign a pointer to a size. If the size is signed, this cannot be done due to its smaller capacity.
Given this, I can't understand the justification. I'm currently using unsigned sizes. If you have anything contradicting, please comment :^)
IMO, this is a better approach than using signed types for indexing, but AFAIK, it's not included in GCC/glibc or gnulib. It's an optional extension and you're supposed to define `__STDC_WANT_LIB_EXT1__` to use it.
int somearray[10];
new_ptr = somearray + signed_value;
or
element = somearray[signedvalue];
this seems almost criminal to how my brain does logic/C code.
The only thing i could think of is this:
somearray+=11; somearray[-1] // index set to somearray[10] ??
if i'd see my CPU execute that i'd want it to please stop. I'd want my compiler to shout at me like a little child, and be mean until i do better.
-Wall -Wextra -Wextra -Wpedantic <-- that should flag i think any of these weird practices.
As you stated tho, i'd be keen to learn why i am wrong!
Why?
By the definition of ptrdiff_t, ISTM the size of any object allocated by malloc cannot be out of bounds of ptrdiff_t, so I'm not sure how can you have a useful size_t that uses the sign bit?
"struct Goose { float weight; }" and "struct Beaver { float weight; }" would remain incompatible, as would "struct { float weight; }" and "struct { float weight; }" (since they're declared without tags.)