Implementing Generic Types in C
btmc.substack.com
btmc.substack.com
When I have such a situation, I'm inclined to write myself my own pre-processor (as I did for Pascal once in a previous millenium, on an Atari ST 520+), so that you can write in a style that is nicer to program in, which gets pre-compiled into something that is nicer to use from your client code.
Nothing comes without downsides: the price of this is that other developers need to understand your idiosyncratic pre-processor, so this method works best for "single author" code/personal projects.
What you don't want in a team is each coder having their own way of doing things, so that you cannot share libraries and helper functions.
BTW, the best book on the OP's topic of production coding in C and implementing type-safe ADTs is Chris Hanson's book "C: Interfaces and Implementations." It contains some eye-opening tricks even for experienced developers in (standard) C.
If you're using C, use bloody C. Not C with weird extensions that nobody will understand. Not C attempting to gussy up into a language with a real type system. Not C with a garbage collector. etc.
I don't get why so many people use C but don't want C. This isn't 1995 where you have to shoehorn everything into the C ecosystem because everything else sucked.
Will it have IDE support for the abstraction? Will a syntax mistake explode with a non-sensical error (yes, in this instance, because you are splicing macros and a syntax mistake will splice weird)? etc.
If you want C with Generic Types, use C++. Rather than splicing unknown garbage onto C, use a subset of your C++ features and you will get full IDE support, proper template errors (okay, maybe not an improvement), proper elision and inlining, etc.
My test for whether something or not is "still C" is very simple... "Does it compile with a C compiler"
These compile. They're C. End of debate.
Whether something has IDE support is neither here nor there. When I was learning C, your IDE was "vi" and "csh"... It was still C.
As for errors, completely normal C code can spew out errors up the wazoo if you miss an errant ;, " or } (to pluck examples out of the air) somewhere important, things like "function not defined" because you were using a function later on in the file and it can no longer be parsed. Error quality does not define the language either.
Fine. My test for whether something consisting of C keywords, variable names, and symbols expected within the language as per the definition by some publicly agreed standard such as "C17", "C99" or "K&R", abhorring any non-standard extensions, other languages, or practices outside of the provided pre-processor, assembler and linker, and excluding any non-standard keywords that may or may not result in correct output, as pertaining to underlying architectural differences, is "still C" is very simple... "Does it compile with said C compiler"
People have different preferences though, so if someone has 50,000 lines of code invested in a C application, but wants to (for some reason) use generics in some way, perhaps this is their best option...
Some say it works; I've never succeeded, even on solo projects.
If I'm using C, it's probably because I'm doing something low level and garbage collector could be problematic there. If you are fine with complexity, C++ or Rust are better options. If you want a simple language, I'd suggest Zig.
On the other hand, if I don't want to deal with memory management, and still want fairly fast compiled language, I'd just go with Go. It has a much better ecosystem, because low level GC language is really mostly targeted at server components and Go excels there with implicit async everywhere.
https://github.com/codr7/hacktical-c/tree/main/vector
The problem was always pretending C is something it's not, that never works well in any language.
At some point, it becomes worthwhile to graduate to a more powerful language that still retains all the low level capability, like DasBetterC.
Don't we all.
Except for the committee, of course, and its entourage.
> Well, except the first one, template macros, where I can’t really find any pro, only cons.
For toy examples, the first (expanding a huge macro) has mostly cons. But it is more flexible when you want to instantiate different parts of the header. The second approach can work but will be clumsy in this case because the whole header is considered as one unit.
Type erasure is still type checked though, it's just lost at runtime (i.e. the generics are not reified).
This works for Java very well because the JIT will have another chance at further optimizations once the application runs.
In the C example ? What do you mean ?
But that's a weird way to put it... Java 5 was when generics were actually introduced... so comparing a C hack for generics to Java pre V5... is just... weird.
Modern Java has generics so it'll check at compile time that you use the types correctly.
I would think that a runtime exception vs memory corruption is a big difference in relative type safety....
But overall I was just trying to explain to folks in thread how things were parsed....
[1] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3212.pdf
[2] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2924.pdf
[3] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2853.pdf
And awesome, I love it!