All this to say: if Rust can make it in, that's great, because I'm tired of dealing with C's simplicity for more complicated tasks. I'm not a rust user but if it lets me use classes and templates, I'll switch over
All this to say: if Rust can make it in, that's great, because I'm tired of dealing with C's simplicity for more complicated tasks. I'm not a rust user but if it lets me use classes and templates, I'll switch over
Yer not switching any time soon then. Rust does have methods but not classes (QOM’s inheritance is specifically called as an issue in TFA) and it uses Haskell-style generics rather than C++-style templates.
You can though, unless I totally misunderstand your syntax
use std::{marker::PhantomData, ops::{Add, Mul}};
pub struct Foo<T> {
_p: PhantomData<T>,
}
impl<N, M> Mul<Foo<M>> for Foo<N>
where
N: Add<M>
{
type Output = Foo<<N as Add<M>>::Output>;
fn mul(self, rhs: Foo<M>) -> Self::Output {
todo!()
}
}Rust decided that it's important to not have instantiation-time compiler errors, but this makes computation with const generics complicated: you're not allowed to write Foo<{N+M}> because N+M might overflow, even if it never actually overflows for the generic arguments used in the program.
Of Simula (& descendants) proto-OOP objects, not as it was envisioned by the one who coined the term.
http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay...
> I didn't like the way Simula I or Simula 67 did inheritance (though I thought Nygaard and Dahl were just tremendous thinkers and designers). So I decided to leave out inheritance as a built-in feature until I understood it better.
> OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP.
Fun that this was mentioned too:
>> it uses Haskell-style generics
> polymorphism via v-tables and composition via traits, that's enough object-orientation for me
For which Alan Kay had this to say:
> My math background made me realize that each object could have several algebras associated with it, and there could be families of these, and that these would be very very useful. The term "polymorphism" was imposed much later (I think by Peter Wegner) and it isn't quite valid, since it really comes from the nomenclature of functions, and I wanted quite a bit more than functions. I made up a term "genericity" for dealing with generic behaviors in a quasi-algebraic form.
In a way it's fascinating to see how C++ has shaped (dare I say warped) the collective vision of how to do OOP.
For example, as a Rubyist, which was heavily influenced by Smalltalk, it is fascinating how hard it can be to explain how fundamentally different message vs call are, and what's the whole point of modules, mostly because the other viewpoint is so warped as to cognitively reject that it can be any different from the C++ model.
It's so much harder to debug things when function calls are secretly macros, and if I didn't have the vscode cpp language server for goto definition, I'd be completely lost. I'd wager that only 5% of the #defines aren't auto generated by occasionally recursive macros. Maybe hyperbole. Makes it really hard to figure out how existing code works.
I completely agree with his point about reimplementing C++ badly in C. GNOME does this too in their libraries. He will be much happier with Rust.
If you read function specs in the OCaml docs, they're understandable and the syntax is clean; the same concepts bolted onto C++ look like line noise, both in actual syntax and the (allegedy) English-language description on en.cppreference.com.
Reading the C++ standard itself is an exercise in futility, very much unlike the C standard. (Although, latest developments in C land are repulsive too, IMO.)
The C++ committee's tantrums and scandals (physical violence at meetings!) put the worst that has been seen in open-source communities to shame. <https://izzys.casa/2024/11/on-safe-cxx/> has been posted to reddit and HN recently.
C++ compilation still takes absolutely forever, and C++ template error messages are as incomprehensible as ever.
One important problem with C++ (repeated ad nauseam, so this is nothing new) is the unforeseen and unintended harmful interactions between such language features that were supposed to be orthogonal. The only remedy for that is to stop adding stuff to the language; but nooo, it just keeps growing.
Another important problem is that, the more the compiler does for you implicitly, the less you see (and the less you can debug) what happens in the code. This is not a problem in OCaml, which is a managed language with a safe runtime, but it's a huge problem in C++, which remains a fundamentally unsafe language. (And yes, once you start extending OCaml with C, i.e., mixing implicit and explicit, OCaml too becomes unsafe, and a lot of head-scratching can occur.) A home-grown object system in C is at least explicit, so it's all there for the developer to read, instrument, step through, and so on.
When your "core guidelines" <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines> could fill a book, there's a problem with your language.
(I'm not here to defend QEMU's object model indiscriminately; I'm here to bash C++.)
There are exactly none, but there was a time during the mid- to late-90s when it was hip to implement OOP frameworks on top of C (C++ only really became a realistic option once C++98 was widely supported which took a couple of years, ...also there was such an extreme OOP hype in the 90s that it is hard to believe today - EVERYTHING had to be OOP, no matter if it made sense or not).
QEMU seems to be from around the end of that era (first release apparently in 2003), and looking at the code it looks exactly as I imagined, with meta-class pointers, constructor- and destructor-functions etc... it would almost certainly be better to use a simple non-OOP C-style, but that was an 'outdated' point of view at that time (since everything had to use that new shiny OOP paradigm).
However there is a reason to have the object model, and it was added because some things were getting out of hand. You had to write three parsers for everything you added: one for command line, one for the text-based command interface and one for the JSON API. And you had to do it once for each kind of "object" (device, network backend, disk backend, character device backend etc.). The object model is designed to let you write code just once for all three and to reuse the interface across object types.
IMHO if you deliberately use C it's because you want to keep things simple. Sometimes C++ will inevitably lead to overcomplicated designs nobody needs; there's a reason why 90's C++ feels massively outdated, while 90's C is still (mostly) accessible. C is a great way to keep the urge people have to shovel in OOP even when it doesn't really make sense. When I see some bullshit C++ code at work, I often think, "if people had to write this in C, they wouldn't have overthought this this much"...
My 2 cents is that there was a Java craze back in the '90s that basically infected all code designed in that time period, and that's how we got some very weird nonsense that in hindsight was poorly thought out. Just look at stuff like GTK+ and GObject...
It's a common thing to do in projects like this in C. While C++ solves some of the problems for larger projects, it brings so many more, it's usually not worth it. Projects of this kind, essentially, invent their own language, with their own conventions. C is just a convenient tool (given the alternatives...) to do that, as it's treated as a kind of a transpilation target. Think about languages like Haskell which essentially work like that, but went even further.
So, what typically happens is that memory allocation, I/O and concurrency need to be done differently from how the language runtime wants to do that. So, at this point, you basically throw away most of the existing language runtime. From my recent encounters with different bugs in QEMU, I imagine that they really, really need a lot of customization in how memory is allocated (my particular case was that QEMU was segfaulting when running ldconfig in aarch64 emulation, which turned out to be due to ldconfig having some weird custom-made memory allocation pattern which QEMU cannot account for.) It's easier to fight this battle with C than it is with C++.
In other words, if you don't want the language runtime, if you don't want C++ templates, smart pointers, classes, lambdas, exceptions etc. But, you want to be able to manage memory for eg. realtime kind of system... simply removing all the unwanted stuff from C++ is harder than it is with C.
And, if you did want some / all of those features of C++, there are saner languages to have that (and you'd pay for it by having to drag in their runtime and conventions). Before Rust was a thing, I've seen people choose D instead, for example, as a kind of middle ground between the asceticism of C and unnecessary excess of C++.
Sure, Rust is better than C++ in some ways. You can have a legitimate debate about C++ versus Rust. There can be no debate about C versus C++: the latter is better in every single way.
> unnecessary excess of C++.
Is the unnecessary excess of C++ in the room with us right now?
Again, you don't have to use any part of C++ you don't like. A few minor spelling differences aside, it remains a superset of C. Using C++ not C will not hurt you in any project whatsoever.
C is simpler because you don't need to remove any of that stuff, because it's not there to begin with.
My argument about Lisp doesn't say that anyone should use Lisp instead, it just points out that people making the choice between C and C++ aren't concerned by how easy it is to build new languages using the language building tools of C or C++ because both are very bad at it. Had the objective of building a new language been the primary objective of such projects, they'd be using a language with superior language building tools (not necessarily Lisp, there are many alternatives, it's just that neither C nor C++ are good at it).
But yeah broadly speaking it's a terrible idea.
It's a full time job on its own to fix all of the compilation errors. Could probably fix it with coccinelle scripts to make some parts easier, but still, validating the codebase with different compilers to make sure there's no resulting subtle breakage either still requires a lot of effort.