6 ms·
Here's the explanation from Linus for C++ http://harmful.cat-v.org/software/c++/linus http://harmful.cat-v.org/software/c++/linus
by swebs 8y ago
Here's the explanation from Linus for C++
http://harmful.cat-v.org/software/c++/linus http://harmful.cat-v.org/software/c++/linus
- jcranmer 8y agoThis is a prime example of the gatekeeping nature of C programming you see: the idea that, if you're not using C, you're not a serious programmer, so go away and play with your toy language. The main actual objection to C++ is that, well, the exception handling model is controversial, and it is especially ill-suited to kernel programming. RTTI can also get thrown in the lump here. Standard libraries aren't going to work in the kernel anyways, and so if you strip that out, what benefits does C++ get you over C? The correct answer, actually, is that you get RAII for added safety, and tooling such as IDEs have better support for finding all implementations of a virtual function than they do function pointers, but for a lot of C programmers, this can be a sense of "real programmers shouldn't need such assistive technology."
- bogomipz 8y ago>"The main actual objection to C++ is that, well, the exception handling model is controversial" I am not familiar with this. Can you way why exactly it is considered controversial?
- Retra 8y agoGoing from memory here, since I can't find any references. I believe there are problems in the handling of exceptions in constructors and destructors, which permeates into other parts of the language such as how arrays are constructed, etc. For one, the only way a constructor can fail is through an exception, and that's pretty heavy-handed for a language which markets itself as "don't pay for what you don't use." So you can't do RAII in C++ without exception handling. (Note that Rust solves this by not even having constructors.) There are also some problems with exceptions being slow code paths, but that probably varies by compiler.
- mehrdadn 8y agoYou can do RAII without exception handling... you just don't throw from the constructor. There's no stipulation you have to throw from your constructor.
- notriddle 8y agoSo your constructor has to be infallible. That means either you can't allocate memory in your constructor, or that all classes that are backed by dynamic memory have to have a tacked-on error state in case the constructor "fails". An operating system can't just crash if memory allocation fails, because it's actually normal for the operating system to run out of memory. If your OS is even a little bit like Windows 95 or System V, it uses all of the "left-over" memory for the file system cache, so every time it tries to allocate memory, it probably needs to flush a page to disk, which takes long enough that the OS will also want to go back into the scheduler to try to find some more work to do while that runs. Ironically enough, C++ has fallible allocation in the standard library in spite of being ill-suited for it (unless you have exceptions; those can support fallible allocation just fine), while Rust went with the let-it-crash approach in its standard library, despite the fact that the language itself allows you to just return an Option from all the functions that allocate memory.
- mehrdadn 8y agoI was just saying you can have an error state, like iostreams already do. It doesn't imply you give up RAII.
- Retra 8y agoIf you're doing that, you will often end up needing an init() method, and you arguably don't get RAII if you're not doing initialization during construction. I mean, you can write safe code this way, but if C++ hadn't backed itself into a corner by trying to make user types act like builtin types, this wouldn't even be a question. Constructors wouldn't have to be a whole domain of study unto itself, they could just be functions and you're done learning about them.
- jcranmer 8y agoThe implementation of C++ exceptions is what is often called "zero-cost exception handling." However, the zero-cost actually turns out to be a lot of cost, just cost omitted from normal accounting. The way zero-cost exception handling works is you build this large table that says "if there's an exception where the program counter is in this range, jump to this point." There's no added code to the program in normal control flow (hence the name), but when you get an exception, you have to do a mildly expensive unwind and table lookup to get to the code. The table itself also comes with the price of requiring extra relocations for all of the PC offsets, which also comes with a startup hit. One of the main costs is that unwinding through a function is assumed for all functions, unless proven otherwise. This means, even if you're not using exceptions yourself, the compiler still needs to generate code to catch an exception after every function call, run all destructors in reverse order, and then rethrow the exception. And remember that destructors and constructors are themselves function calls, which therefore might throw exceptions as well. Generating the code to actually do all of this destruction correctly requires adding code to the function to handle this that is not in the normal execution path, which can also cause issues with things such as instruction caches. A final issue is that exception handling requires RTTI to be able to dispatch to different catch handlers. RTTI needs to be generated for every potential type (this includes POD structs, for example), which is again a codesize bloat issue. For types that have virtual functions, the RTTI information need to be included in the vtable, which means they're not going to be eliminated by dead global elimination passes (as unreferenced POD structs would tend to be). In short, RTTI and exception handling require a lot of extra tables that have to be generated even if you don't use them. Furthermore, a major concern for kernels, exception handling requires unwind support, which is generally not part of the kernel library repertoire and can be tricky to do manually. Compiling without exceptions and without RTTI is not unusual for large C++ applications for these reasons.
- int_19h 8y ago> This means, even if you're not using exceptions yourself, the compiler still needs to generate code to catch an exception after every function call, run all destructors in reverse order, and then rethrow the exception. For every non-inlined function call (or one that it otherwise knows additional information about, e.g. with LTCG). For C++, this is a very big difference, since idiomatic C++ has a lot of inlined functions.