3 ms·
C sounds nice if your task is simple enough, or at least if you can decompose this to a series of loosely-connected simple-enough tasks. But sometimes, there i
by theamk 9mo ago
C sounds nice if your task is simple enough, or at least if you can decompose this to a series of loosely-connected simple-enough tasks.
But sometimes, there is an inherent complexity in what you are trying to implement, then C becomes way, way complex than C++. You build stuff, and there is so many manual steps, and none of them must be missing, or things will subtly break.
A good example is "gstreamer", the multimedia streaming framework. It is implemented in pure C. It needs to use basic data structures, so it uses GLib. It also need to support runtime-defined connection graph, so it is built on top of GObject.
Yes, build times are amazingly fast. But you pay for it - look at something simple, like display_name property[0]. There is display_name member, and PROP_DISPLAY_NAME enum, and switch case in setter (don't forget to free previous value!) , and switch case in getter, and it's manually installed in class_init, and you need to manually free it in dispose (except they forgot this).
So many places just for a single property, something that would have been 1 or 2 lines in a well-structured C++ app. And unlike C++, you cannot make simple rules like "never use char* except for 3rd party libs" - it's all those multi-point checklists which are not even written down. A
[0] https://github.com/GStreamer/gst-plugins-good/blob/master/sys/ximage/gstximagesrc.c#L931 https://github.com/GStreamer/gst-plugins-good/blob/master/sy...
- acuozzo 9mo ago> You build stuff, and there is so many manual steps "The real goal isn’t to write C once for a one-off project. It’s to write it for decades. To build up a personal ecosystem of practices, libraries, conventions, and tooling that compound over time."
- rramadass 9mo agoRight. The article is well written and the author has touched upon all the points which make C still attractive today.
- theamk 9mo agoYou mean, you are not worried about high complexity of codebase because you work with it every day for decades, so you know all this complexity by heart? This basically requires one to be working solo, neither receiving not sharing the source code with others, treating third-party libraries as blackboxes. I guess this can work for some people, but I don't think it would work for everyone.
- acuozzo 9mo ago> so you know all this complexity by heart? No. It's that you've built up a personal database of libraries, best-practices, idioms, et al. over decades. When you move on to a new project, this personal database comes with you. You don't need to wonder if version X of Y framework or library has suddenly changed and then spend a ton of time studying its differences. Of course, the response to this is: "You can do this in any language!" And you'd be right, but after 20 years straight of working in C alongside teams working in Java, Perl, Python, Scheme, OCaml, and more, I've only ever seen experienced C programmers hold on to this kind of digital scrapbook.
- theamk 9mo agoI don't see how this can work. You have your personal string library.. and you move to a new project, and it has it's own string library (it's pretty much given, because stdlib C library sucks). So what next? Do you rewrite the entire project into _your_ string library, and enjoy familiar environment, until the next "experienced C programmer" comes along? Or do you give up on your own string library and start learning whatever project uses? And this applies to basically everything. The "personal database" becomes pretty useless the moment the second person with such database joins the project. (This is a big part of Python's popularity, IMHO. Maybe "str" and "logger" are not the best string and logger classes in the world, but they are good enough and in stdlib, so you never have to learn them when you start a new project)
- flohofwoe 9mo agoIt's not the "string library" that's important, but standardized interface types - so that different libraries can pass strings to each other while still being able to select the best string library that matches the project's requirements. In C this standardized string interface type happens to the poiinter to a zero-terminated bag of bytes, not exactly perfect in hindsight, but as long as everybody agrees to that standard, the actual code working on strings can be replaced with another implementation just fine. E.g. a minimal stdlib should mostly be concerned about standardized interface types, not about the implementation behind those types.
- flohofwoe 9mo ago> It needs to use basic data structures, so it uses GLib. It also need to support runtime-defined connection graph, so it is built on top of GObject. That's running into the age old trap of trying to shoehorn an OOP system into C, just don't do that ;) E.g. don't design your systems around the OOP paradigm in the first place.
- flomo 9mo agoUnfortunately your insightful comment is 30 years too late. You'll have to find a time-machine and go back to the 1990s and tell GNU/GTK/Gnome/etc that they are doing it wrong.
- theamk 9mo agoGood luck making any sort of UI without OOP-like methods. The moment you have grouped state (say "button_enabled", "button_shown", and "button_checked") and corresponding methods, you get something OOP-like. The only way to work around is immediate mode UI, but this requires fairly powerful CPU, so it's only feasible on the modern machines. Certainly not something that people would want about 30 years ago, they still cared about performance back then.
- wasmperson 9mo agoImmediate mode UI doesn't require a powerful CPU and was invented in 2002, so about 24 years ago. I think the belief that it necessarily sacrifices performance is somewhat of a misconception. Compare a hypothetical "Immediate Mode" counter: void render_and_handle_button_click(struct ctx *ctx){ draw_text(ctx, "Count: %d", ctx->count); if(draw_button(ctx, "Increment")){ ctx->count++; } } To the equivalent "Retained" counter: void render(struct ctx *ctx){ set_textbox_text(ctx->textbox, "Count: %d", ctx->count); } void handle_button_click(void *ctx_in){ struct ctx *ctx = ctx_in; ctx->count++; render(ctx); } void init(struct ctx *ctx){ ctx->textbox = create_textbox(ctx); ctx->button = create_button(ctx); set_button_text(ctx->button, "Increment"); set_button_click_handler(ctx->button, ctx, handle_button_click); render(ctx); } The only difference I see here is whether the stateful "cache" of UI components (ctx->textbox and ctx->button) is library-side or application-side.
- 1718627440 9mo agoPeople point to GObject say it's complicated and compare it to C++ classes. But the same thing as C++ classes in C would also be far simpler. GObject is so complicated, because it is essentially runtime creation and modification of classes (not objects, classes). Doing that in C++ will also be some work and look ridiculously complicated.