We need a "C++ lite".
We do this by moving features from C++ to C, bit-by-bits, until C become bloated.
It this point, if I want generics, I'd rather just use C++.
... is somewhat understatement.
At least in C there's a possibility to learn from C++'s mistakes, and only integrate the 'good parts' (and the C++ template system certainly isn't one of those good parts).
That's what Rust looked like in the beginning - a better C++. But that seems to be getting corrupted by a bunch of language designers too now.
Showing up at WG21 is much more appealing to most folks.
Maybe compare how C23 compares to C89, and future roadmap.
Also, Ada has a very decent interop with C. To the point that w/o any prior experience, I needed to use some functions from SQLite that weren't already exposed through Ada, and it was a pretty smooth sailing: very little "glue" code, all code that connected C and Ada was written in Ada.
Personally I am not a big fan, because it doesn't have a good story for use-after-free (other than adopting similar analyser tooling like C derived languages), if I want to type @ all over the place I can use Objective-C, and the community doesn't seem very keen in supporting binary library distribution.
That is me, we don't have to like all the same things.
Even with UAF caveat, it still better than plain old C.
Personally I find Odin more appealing than Zig.
The only thing I'm confused about is why more C standards keep coming out. C is what it is. Work with the archaic parts of it as need be. You use C for ultimate cross-platform compatibility (every exotic platform and its mother has an ANSI-C/C99 compiler). If you're able to run on the most bleeding-edge C compiler supporting the most recent WG version of C... then why not just use another language? I say this as someone who loves C. If there are bits that feel old, you just create a DSL to get around these problems that compiles down to C... but you don't change C itself.. Just my perception working with all this for many years now.
Now there are languages like Odin, Zig and C3 that are trying to fill that niche but having C evolve slowly to accommodate such users is also a great idea.
C99 added Designated Initializers which IMHO is probably the most innovative declarative syntax to initialize anything and it took C++ roughly 20 years to adopt such a thing (and then they had to nerf it).
Just as with stdbool.h before, there could be a stdlib header which wraps those internal names into something more human-friendly.
Tangentially related, the macro-based containers you've written here [1] are the best answer for type-generic containers I've come across. One "gotcha" is the container name must be a valid C identifier otherwise it doesn't token paste correctly (see Example #2 of your REAMDE where you typedef'd string* as string_ptr to workaround this). Would you give consideration to a new preprocessor mechanism for concatenating a list of tokens into a single valid C identifier? i.e. Something like CONCAT(struct Foo *) would produce struct_Foo_Ptr? The result is guaranteed token paste-able.
I don’t understand your second point.
>Q: Why does C3 require that types start with upper case but functions with lower case?
Hard pass.
> Avoid "big ideas".
``` Using a run-time value of type _Type T in _Var(T) can be allowed in general (and is useful), but needs to be restricted to contexts where full type information is not required at compile-time. ```
Semantic rules that conditionally apply only in some contexts is a common tendency of the C++ standard that many C programmers often dislike.
(a) it is desired that _Var(T)* and void* be compatible;
(b) however. pointers-to-T for different T are not guaranteed to be compatible in general,
as a consequence, it is NOT guaranteed that _Var(T)* is compatible with T*, which is a theoretical wart worthy of C++. I can see it becoming especially annoying if you’re trying to introduce _Var(T) polymorphism into a code base that currently uses preprocessor macros that textually substitute types.
Perhaps the better solution would be forgo giving void* any special status whatsoever. However, that means that this type of polymorphism can’t be implemented using void* as a polyfill.
It's worth keeping in mind that code aesthetics is an important aspect of C codebases. There's a lot of C code that is exclusively lowercase, sans the macros. So introducing keywords like _Type and _Var will serve to hinder their adoption, because it'd make the code that much more "ugly". Just like what happened with _Generic - a reasonable feature, bad keyword selection -> barely any field use.
Typically, a <stdkeyword.h> header is included that contain macros to provide the lowercased variants. I.E., this is how _Bool was implemented; <stdbool.h> provides the lowercased `bool` variant.
You can't easily lowercase _Type and _Var, so practically speaking it will take years before these features could be suitable for wide-spread adoption. Hence my original comment - given the friction, is it worth expanding the language this way at all then?
Is it C89 or gnuc89? gnuc89 is the one that allows // for single line comments, I don't know what other features gnuc89 adds over c89.
I also call C89 ANSI C :)
Are you guys using gcc or clang?
Basically you can do something like this in c :
var value;
value = data(100);
// or
value = data(0, 2, 3, 4, 5, 6);
int i;
int *ip;
typeof(i) *i;
_Var(_Typeof(i)) *i;
What? How are "int i" and "int *ip" equivalent?_Var(_Typeof(i)) ident strikes me as incredibly silly. The type value produced by _Typeof(i) should be directly usable as a declaration specifier, without requiring hoisting to a name.
FFS, GNU C has had a sane typeof working for years.
To bind a type to an identifier, we don't need a _Type type at all. typedef does this!
I.e. not:
_Type x = int;
but typedef int x;
Existing, decades-old typedef is how you bind an identifier to a compile-time type value. There is no need to do this via a _Type type specifer, which then forces us to move the type we want into the initializer. We use the typeof storage class specifier, which then lets us have the type we want as another specifier, and we don't need an initializer.This works in GNU C:
int x;
typedef typeof(x) tx; // same as typedef int tx;
The problem of declared, named containers to capture type is long solved.