So Casey thinks C sux?

https://twitter.com/cmuratori/status/1494431326325260305

Idk why but always assumed he liked C overall. Don't recall having watched HMH videos of him bashing C.

He has mentioned issues with C many many times during HMH videos. Only place where he said that he liked C probably is in context of C++ - that C is better than C++.

Otherwise C is not great, it has ton of problems. That said, we don't have anything much better right now.

Otherwise C is not great, it has ton of problems. That said, we don't have anything much better right now.

I'm not good enough to know what are the obvious flaws besides "hey I could write this thing like this here" sort of thing.

So let me go on a sort of ramble to try to understand what exactly the issues are.


I honestly do like to use C as subset of C++, with the C++ syntax + other features (e.g. foo() and not foo(void), printf() and not cout<< [stuff], etc).

I know things like printf() are not the best thing, but comparing to some boost thing, or even cout<< [stuff], I much prefer this because I know what is going on, the ugly doc stuff is like a couple of reads and occupies like 2 pages (and applies to that family of prints). Boost? I feel stupid half the time I need to read documentation tbh, same thing for some of the STL docs. I hate the garbled boost libraries, it is such a conundrum. Even to make a simple clock, it is such an ugly thing.

This is what I dislike about C++, having to read documentation to find out I need to/can do things like cout << [stuff] << std::hex, or how that such and such class will be instantiated in whatever way it is pure waste of time. In the former case, I sure need to bcz no way one could have just deduced whatever the overloaded operator is defined to do.

It feels like people bloat it with "features" just because.

So these things are what I dislike in C++, it feels it is so unnecessarily complicated, and C feels like too simplistic.

But now I'm trying to understand, what exactly, beyond these things, would a low level language have to have? Because in the worst case scenario, people could just re-use code and circumvent most of these problems, which brings me to what would be a really cool STL/Boost.

Imagine a "lean" STL non-OOP oriented, not garbled like boost, just pure functions, documentation explaining signatures, etc, where you do the least amount of typing and you get the friggin highres clock multiplatform (or just call respective separated uncs), you start a network connection and get DNS easy, etc. That is to say, imagine if STL was actually quite big, included networking, database, AND everything was like printf().

Is this more or less what people like Casey would prefer? Or the thing is really deep and advanced stuff?

Cheers Martins!


Replying to mmozeiko (#25981)

The problem any new languages will have is network effects. People will stay on something that is bad, because lots of people are still on it. So hardly anyone is having a good time, but they can't bring themselves to leave.

I've noticed the problems with C++ so much later on after I started learning it, they are some cultural phenomena which are complex, but are inherently human:"techies" are in no way better suited to not exhibit herd behavior.

So after some years going on forums and asking questions and seeing over and over people talk about "good practices" over and over, even getting pissed sometimes, I understood that there are many people highly specialized in the documentation, perhaps more so than coding, and emotionally attached.

They think that you have to use a new feature in the new spec, or that you have to program full OOP, etc. They also think the language has to inject new things on it frequently.

After you see it as dogmatic behavior, you can't un-see it.

That said, I think C++ is cool. I like to learn it. Want to learn more of it.


Replying to Shastic (#25982)

Is this more or less what people like Casey would prefer? Or the thing is really deep and advanced stuff?

No. That's pretty much opposite what wants. Language should not have to deal with stuff like this. Leave library as separate as possible. Language should come with minimal runtime, just whatever compiler needs, and no OS abstractions. There are too much difference in different OS & versions, and different programming models to abstract it under one API. Leave interaction with OS to programmer themselves - they can decide on whatever API to use better. Like in network case there are so many choices, from simple single threaded blocking IO, to nonblocking socked, or one thread per connection, to more advanced concepts - overlapped I/O , IO completion ports, io_uring. They all require different structure of your code. And often get obsolete once different API gets introduced in OS. So don't force it to users of your language. Just offer tools to allow make them this themselves.

What Casey wants he mentiones in that tweet replies - no UB, more sane macro support, some reflection/introspection, better SIMD.

Add few extras like declare anywhere, no . vs ->, sane symbol linking/dlls, maybe better type promotion, more control over optimizations/codegen and similar things and you will get really usable language.


Edited by Mārtiņš Možeiko on
Replying to da447m (#25989)

Thx for the answers!

No. That's pretty much opposite what wants.

Ok!

more sane macro support,

Not sure what this means, what other macros exactly? Things to make compiler build in this or that way?

some reflection/introspection

I thought C++ had some of this: #include <typeinfo>?

better SIMD.

Isn't this already there but need some special macros depending on compiler? In HMH code there are various SIMD'ed abstracted types there. How would be possible to make them better?

Add few extras like declare anywhere,

What is "declare anywhere"?

no . vs ->

How will you have automatic memory alloc for the structs then? Isn't this essentially the difference here? Would he then prefer that only -> is used and the memory is automatic by default, but user can override this and do herself the mallocs, deleting pointer, etc? Don't get this one.

maybe better type promotion

What does it mean? If I want to cast type A to B?

more control over optimizations/codegen and similar things and you will get really usable language.

Do you mean build systems? Like generating this or that h/cpp files depending on some compile time flags?

Not sure what this means, what other macros exactly? Things to make compiler build in this or that way?

Current macro implementation is is pretty poor. Try writing macro that accept arbitrary argument count, or iterates over arguments to do something. Or what about multi-line macros. What about debugging them (stepping line-by-line), etc... With better reflection system in place there probably wouldn't be need for macros anyway.

I thought C++ had some of this: #include <typeinfo>?

That's only RTTI, which is pretty much nothing (and nobody uses it seriously). Reflection means you could get list (or iterate) over fields of struct to do something. Maybe get type of existing function, introspect argument count/types and build new function as wrapper over existing with some extras. Check what C# offers for this. It is pretty good.

Isn't this already there but need some special macros depending on compiler? In HMH code there are various SIMD'ed abstracted types there. How would be possible to make them better?

If you look at details of SIMD intrisics there are a lot of inconsistencies - argument order, load/store pointer types. Mess with __m128 vs __m128i types when instructions don't have this kind of differentiation. That all should be cleaned up. Clang's ext_vector_type is good step in this direction. But more can be done there.

What is "declare anywhere"?

So you could write code:

void f() {
  g(); // call g() that is defined below;
}
void g() { ... }

No need to declare functions above their usage.

How will you have automatic memory alloc for the structs then? Isn't this essentially the difference here? Would he then prefer that only -> is used and the memory is automatic by default, but user can override this and do herself the mallocs, deleting pointer, etc? Don't get this one.

Memory allocation has nothing to do with this. It's just about simpler syntax:

void f(Vec2* v) {
  v->x = 1; // why you need to write this?
  v.x = 1;  // instead of this?
}

There's no confusion what would need to happen with v.x syntax when v is pointer in C.

What does it mean? If I want to cast type A to B?

In C automatic type promotion is a mess. Like 1 has int type or a+b and similar expressions promotes arguments to int, when a/b are short/char. A lot of could be cleaned up there.

Do you mean build systems? Like generating this or that h/cpp files depending on some compile time flags?

Not build systems. Just control over what compiler does with your code - when it inlines or not, when it vectorizes loop or not, etc... Give more control to user than automatically do something without you knowing it.


Edited by Mārtiņš Možeiko on
Replying to da447m (#25993)

Current macro implementation is is pretty poor. Try writing macro that accept arbitrary argument count, or iterates over arguments to do something. Or what about multi-line macros. What about debugging them (stepping line-by-line), etc... With better reflection system in place there probably wouldn't be need for macros anyway.

Why won't you just write a function?

Reflection means you could get list (or iterate) over fields of struct to do something. Maybe get type of existing function, introspect argument count/types and build new function as wrapper over existing with some extras.

Ok let me try to understand: I have some runtime process going on, and if 0 I make struct A, if 1 struct B. This is the only use-case I can think of rn. So it would be useful to know the type in some application I'm ignorant, but wouldn't that be a minor convenience more than anything else? (like I could just keep the process flags to know which struct I've built.)

For what concrete case I want the language to give me a type I somehow don't know what is?

In C automatic type promotion is a mess. Like 1 has int type or a+b and similar expressions promotes arguments to int, when a/b are short/char. A lot of could be cleaned up there.

Yes I agree, but always assumed this is a default because of the 32 bit machines, casting smaller int types to the mem size actually makes sense. But yes, sometimes I do not want that int conversion.


Replying to mmozeiko (#25995)

Why won't you just write a function?

That requires using specific types. What if I want "print" function that prints any argument passed to it, with any type? Cannot do that in C in a good way.

For what concrete case I want the language to give me a type I somehow don't know what is?

Serialization, for example.

Let's say you have struct Foo { int x; char str[10]; }; and you write function void Write(char* buffer, Foo* obj) - where it takes obj and memcpy each field of struct into buffer. But then you need it also for struct Bar and struct X and so on... With reflection you could write such function just once, enumerate each field and do specific operation depending on type. Obviously generated code won't have any loops or if statements, just plain memcpy's or whatever.

Or "print" function from previous answer - that prints out type recursively printing out each field of all members if they are also a struct.

This is basically metaprogramming - creating new code based on existing code/markup. It's not about doing something with type at runtime, it's about what you can do at compile time (similar to C++ template arguments).


Edited by Mārtiņš Možeiko on
Replying to da447m (#25999)

it's about what you can do at compile time (similar to C++ template arguments).

OK but then you know for sure what the types will ever be? So what is the advantage besides compressing code like in templates?


Replying to mmozeiko (#26000)

Advantage compared to what other ways?

Advantage over manually writing N similar copies of same function is that your code stays simple and small. Advantage over doing codegen in separate program/script is that maintenance is simpler, no need to go to other places/scripts/languages, everything stays in same place.


Replying to da447m (#26001)

doing codegen in separate program/script

Curious. How does this work? How do you do code generation inside your own code? What knowledge of the type (reflection) has to do with this?


Replying to mmozeiko (#26002)

Jai does this with #run: https://github.com/BSVino/JaiPrimer/blob/master/JaiPrimer.md#arbitrary-compile-time-code-execution

In C# you can do similar things with DynamicMethod/ILGenerator & System.Linq.Expressions.

In C++ you do similar things with templates (they are way less capable).

Type knowledge is what allows to do interesting things on your types & structure of code.

For my previous serialization example, you need type information to actually iterate over members of structure. Otherwise how you would get each member if you don't know type of structure?

Another example - let's say you want to log all your OpenGL calls. So for thing like glEnable(0x123) you want to write 0x123 to file. Your metaprogramming code could get all gl* functions, for each function iterate for all arguments and emit code to serialize/print it, then emit new wrapper GL functions that you actually call at runtime. Getting function arguments and their types is what makes this possible.


Edited by Mārtiņš Možeiko on
Replying to da447m (#26003)

The author of C3 language has some pretty cool ideas for a macro system.

https://dev.to/lerno/an-evolution-of-macros-for-c-59b5

https://dev.to/lerno/macros-in-c3-a-status-update-1m1n

Casey had a rant where he also explained the usefulness of iterating over members of a struct.

The author of C3 language has some pretty cool ideas for a macro system.

https://dev.to/lerno/an-evolution-of-macros-for-c-59b5

https://dev.to/lerno/macros-in-c3-a-status-update-1m1n

Casey had a rant where he also explained the usefulness of iterating over members of a struct.

thx useful stuff;


Replying to longtran2904 (#26006)