C++ - The Forgotten Trojan Horse
ejohnson.blogs.com
ejohnson.blogs.com
http://yosefk.com/c++fqa/defective.html
The rest of the FQA site is good too, but the summary is the best enumeration of all the brain damage.
It helps to remember, that C and C++ don't exist on an "VM island", i.e. as an isolated process, like all high-level programming languages do. They are effectively extensions of UNIX-derived operating systems, i.e. they run under "UNIX VM". They even have a garbage collection of sorts: when you exit the program, the memory gets freed. And, according to Linux philosophy, launching processes or forking/dying should be quick, efficient and, in fact, desirable. Also, having many independent binary modules is the norm, so here is your runtime encapsulation.
But C family is still the only tool out there that allows you to extend an OS, i.e. for instance to build a piece of code, a single instance of which will be serving all other simultaneously running processes. This explains why Apache eats so little RAM. None of VM-based languages can do that. It's called systems programming.
Building a web-based CRM in C++ is stupid. Building a filesystem in Python is impossible.
Allow me to clarify: C++ is a replacement for C. C code rewritten as C++ will almost always be shorter and the same speed. The structure and function of the code will also tend to be clearer in C++.
To put up with the pain that is C++ mangling (which I sometimes do), I need to be using major features of C++. If you're only using C++ to make shorter, prettier code that could just as easily be C, you're creating an unnecessarily fragile binary.
Clearly C++ has its advantages, I'm just saying that you should require a lot of those features before accepting it as a language. This is especially true now that C has been absorbing some of the advantageous features of C++, such as inlining.
I find this statement to be rather untrue because I think most modern compilers when forced to be ISO/ANSI compliant (i do: g++ -ansi -pedantic) ensure your code is portable across systems.
Later I was able to get my program to compile on Visual C++ with absolutely no modification.
I was referring to the binary interface. What I'd want to do (and generally can with C) is compile a library with any compiler, and link it with any other code, with no recompiling.
You see this all the time with Unix, say; maybe Sun compiles a bunch of stuff with their C compiler, but you can easily link to what's in /usr/lib using GCC. But with C++, it's typical for the loader to not link C++ libraries that were built differently; and if you're really unlucky, the loader won't notice and instead you'll see subtle binary errors at runtime.
"When an invalid program finally crashes (or enters an infinite loop, or goes to sleep forever), what you're left with is basically the binary snapshot of its state (a common name for it is a "core dump"). You have to make sense of it in order to find the bug."
If you write an invalid program... Then you are doing it completely wrong! (http://lemonodor.com/archives/2007/10/youre_doing_it_wrong.h...)
There are two types of peope: those who belive what they read and those who want to try out their own and then read and after that whine or ... :)
The whole list is a one big whine that in parts is staggeringly clueless. It reads like its author has challenged himself to come up with as many "Defective" or "Very complicated" items as he could just for the sake of making the list looooong.
http://thread.gmane.org/gmane.comp.version-control.git/57643...
I'm sure if he shared his actual thoughts, it would bring him some serious hassle, what with people wanting to debate with him. Much easier just to make it clear he isn't willing to be reasonable on the topic.
While I completely agree that using C++ in the kernel is not the brightest idea, I am curious what's so fundamentally broken about C++ exception handling. It certainly comes with performance penalties, sometimes considerable (especially on the fast paths), but why is it flawed fundamentally ?
Almost makes sense to me, except any feature in C and C++ can be criticized this way. In the C/C++ land you have access to hardware, plus almost every resource (except maybe function stack frames) is allocated and deallocated manually. In C/C++ you can make a mistake with virtually any operation that involves pointers, buffer overruns being perhaps the most popular one. So? Dump pointers altogether?
Or take "return" - basically same problem as with "throw" (although only within one function) - it can bypass some resource deallocations.
C/C++ is like a scalpel, it requires professionalism and great, great care and you can do wonderful, magical things with it.
There are of course a few things wrong with this design.
One is that even deeply nested exceptions count; so if you've vetted your code and listed every exception that you throw, you're still screwed if you call a function that eventually throws something else.
This of course leads to the next problem: code evolves. Suppose you did somehow review all possible code that your function will touch, and wrote an accurate throw-list. Then next week, Jim goes into one of those buried routines and makes it throw something new; boom, you are once again at risk of terminating.
Yet another problem: suppose the messed-up throw list is in a library. Not only does this mean a library is responsible for exiting your program at a completely inappropriate point, but it might be a library that you can't easily change. So you're forced to declare implementation-specific exceptions that have no meaning to your callers. (Or just don't declare, as I do.)
The real issue of course is that terminating is a stupid behavior, even though they define terminate() to give you a point of interception. There are all kinds of reasons why you might accidentally mess up a throw-list, and since all you really gain for "fixing" this is a little code purity, it hardly seems worth dealing with severe runtime errors.
There are zillions of corner cases - things like handling of exceptions thrown from destructors (remember, destructors get called implicitly as C++ unwinds the stack, possibly due to ... another exception) that get so complex and weird it is barely possible to understand, let alone correctly code for them.
It does use the Java virtual machine, however, which allows it to use existing java libraries.
(That is, as long as Clojure code can be deployed easily as jars, and as long as Clojure doesn't create any special classloader problems because of its runtime system. Those are problems that bit me hard when I used Jython code in the Eclipse plugin framework, and I imagine any dynamic language might accidentally create classloader problems unless it was specifically designed to avoid them.)
Great post, but the comment spam shows that this site isn't very well maintained. Clearly the author hasn't spent much time on it in a long time.
Too bad.
And in the end...it's not such a bad language. In the real world, you only use a small part of it on any one project. In the real world, your framework or libraries matter more than the language anyway. And in the real world, performance always matters in the end.
Joe Hacker might not care about performance of his scripts on his VM he's got from a hosting provider, but you can bet they care a great deal about how many VMs they can fit on one physical host.
Kind of disappointed in HN for rating you up. Just goes to show...