The Dark Side of C++ (2007)
fefe.de
fefe.de
Yes, it's true. && might do something different than you might expect if you're a C programmer. If you're a C++ programmer, then you know somebody might have overloaded the && operator. Yes, they could do it in some weird and unexpected way. Just like somebody can declare a weird and unexpected macro in C.
Yes, people who define C++ classes you use can screw you with dangerous or incorrect behavior, like throwing exceptions from destructors. Just like people who write C libraries can screw you with dangerous or incorrect behavior.
auto_ptr is not useless. At least, I assume it's good for something. Who knows? If not, who cares? boost::shared_ptr is "poor man's garbage collection," not auto_ptr. If you cite the documented behavior of a type as a huge and unwelcome surprise, then gosh, I know exactly how to avoid surprises like that: read the freaking documentation before you use the type.
Oh, and again with the "I can't tell what this code does." Here's a crazy scenario in C: call a function. Do you know what the code does? No, you don't. How awful! Unless you look at the function you just called, or maybe just glance at its documentation.
Half of these criticisms of C++ boil down to, "It's okay for functions to have unknown behavior that I have to learn about before using them, because I'm used to that, but it's not okay for types to have any kind of unknown behavior, because I'm used to C, where that is not the case."
It is not an issue of behavior but of understanding.
In C++, a large number of things can be happening in a single line: an operator being called, a type conversion, a template instantiation, an overriden function in one of a dozen of classes, a constructor called in some part of the hierarchy, in some namespace... who knows. You have to be smart enough to figure out what is going on in each of these cases. As you code base grows, this makes it increasingly hard to understand what is going on.
Also, you forgot macros in your list of gripes, as they can stomp on literally ANYTHING indiscriminately, despite being declared far away from the actual invocation point.
Haskell seems to go a long way towards solving this problem in my humble opinion. Meaning it can have very dense code but as long as you follow the types it is often fairly straightforward to figure out what a piece of code does.
The worst part about all the things that can happen implicitly in C++ is that they can all have side effects! Oh fun. This is not just a pitfall of C++ either, Java/C# can have the same issue - one statement can trigger a whole plethora of side effects making a large brownfield development project one hell of a nightmare.
As a Haskell fan, I have to say that there is a bag of hurt here as well: performance in the lazy evaluation regime. It can be very hard to predict/understand when thunks are evaluated and the effect it has on heap use.
I am still hoping for a strict, pure language with monads to go mainstream enough to use it.
The next Haskell will be strict - Simon Peyton Jones
Source: http://www.cs.nott.ac.uk/~gmh/appsem-slides/peytonjones.ppt
I'm not sure why after finding that he decided to file a bug report rather that not touch any of that code.
Glib goes into great lengths to avoid namespace conflicts so it defines the GBoolean type and G_TRUE/G_FALSE. Whether that really is better than #define TRUE/FALSE, I don't know.
operator[] adds a member to a map if it does not already exist
The main theme of this article seems to be "C++ gives you too much flexibility," but this is a case where C++ is enforcing a default behavior and limiting your flexibility. In Python, for example, understanding code that queries for a key in a dictionary requires you to know some context about whether that dictionary has a default value for missing keys. For all the quibbling the OP does about operator overloading, I'd expect him to be grateful for the predictable nature of map's operator[].
local static initialization is not thread safe
This is just wrong: http://stackoverflow.com/questions/1270927/are-function-stat...
Classes that implement __getitem__ can behave however they want when you use brackets on them. See http://docs.python.org/reference/datamodel.html#object.__get...
Most of the bitching is about language features which exist. If you don't like auto_ptr then DON'T use auto_ptr! C++ at least allows you to ignore features without paying a penalty. Which is why it has a virtual keyword; and does not make every method virtual like Java.
The at() versus operator[] is also an example of the above: bounds checking can get expensive in a tight loop.
It seems that the author has never seen a large, well-written C++ project. In fact, that is the case with many such C++ bashers (I used to be one too). Once one sees how C++ makes it easy to manage a 500000 line project, they will probably think again.
That's pretty unfair. It's the language's complexity that makes the compiler task almost impossible. Before clang came around, most C++ compilers had really shitty error messages, gcc, icc, and microsoft's indiscriminately.
Otherwise it is a lot like saying Java is bad because it is hard to write a good VM.
I doubt that, I suggest search a bit for his name and find out what he is doing for a living...
Exception safety is achievable (pthread_mutex_lock example) if you wrap resources with RAII classes. If he called this "difficult to use C libraries while retaining exception safety" it would be more accurate.
The iterator criticisms are applicable to C iterators too (represented by pointers or user objects).
The various things `baz = foo->bar(3)` could mean are interesting. Some of them are reasonable, if you assume some sort of consistency. It may make audits hard, but the fact it might use implicit cast converters there should not make it harder to read if you are allowed to make the most basic assumptions about what an automatic cast operator would do.
I enjoyed it, nonetheless.
If new[] throws an exception, this leaks
a file handle and deadlocks the next caller.
Poorly written code is buggy? No shit, Sherlock! — to paraphrase the OP.The Frequently Questioned Answers has good, insightful criticism of C++ is here: http://yosefk.com/c++fqa/
I don't understand why people do this. C++ has not abolished pointers; it's still possible to require the &, and it increases readability considerably; why ever use references (to value types; objects are different) as function parameters?
But there is a utility in allowing references to exist in the language for overloaded operators, so you can overload a subscript in a collection and assign to it, such as
foo[x] = bar, where foo is a collection of bar objects.
They could have restricted references to just these sorts of use cases and when using foo[x] as an argument, require the user to type &foo[x], but once references are in the language allowing them as parameters seems like a reasonable enough decision to me.
I think most of the complaints are still valid even with C++11. Whether the complaints are worth bothering about is another issue: C++ is hard, yeah, it's not going to get easier. Personally I wouldn't ever start a new project in C++, I'd rather use C or Go or Obj-C if I need low-level speed and there's a plethora of high level languages (that can still connect with C if needed) if I don't. I nevertheless have to know C++ (non-11) for all the older projects out there, most of which probably won't move to standardize on 11 making 11's benefits moot.
Yep, lots of projects out there that if I am lucky have moved up to Centos 5. When they get C++0x where all of these issues are fixed is just this side of never.
Oh wait a minute. Did I write C++0x? I mean when they get C++11 when all of these issues are fixed.
warning: format '%d' expects argument of type 'int', but argument 2 has type 'std::_Hashtable<std::pair<unsigned int, unsigned int>, std::pair<const std::pair<unsigned int, unsigned int>, GraphNode>, std::allocator<std::pair<const std::pair<unsigned int, unsigned int>, GraphNode> >, std::_Select1st<std::pair<const std::pair<unsigned int, unsigned int>, GraphNode> >, std::equal_to<std::pair<unsigned int, unsigned int> >, std::hash<std::pair<unsigned int, unsigned int> >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, false, false, true>::size_type'
After a quarter century, is it really that difficult to use the type I wrote rather than this hideous expansion?
I don't think that rebalancing the tree invalidates iterators (other than the deleted element [if any] that is)
Anyways, right at the beginning of the presentation you see the quality of his arguments as he uses a very known problem of a particular compiler (GCC) to bash the language. Sort of a straw man fallacy.
The fact that it took gcc programmers 10 years to get to a point where gcc doesn't use ridiculous amounts of memory when compiling few lines of tricky code is a fact in favor of the argument, not against it. Gcc programmers are smarter than an average programmer. If it was easy, they would have gotten it right on the first try. Accordingly, C or Java or C# or Go compilers didn't have such problems so C++ is more complex for compiler writers than pretty much every other known language.
The same goes for error messages. In the past decades we've had tens of C++ compilers. If clang is the first one that is able to generate decent error messages for templates, then it means it was a hard problem to solve.
None of that got any easier: if you want to write another C++ compiler, it'll still be hard for you to generate decent error messages or compile tricky C++ code reasonably.
Clang, written in C++, is faster, uses less memory, produces better error messages, and, I brink, took less calendar time and man hours to write.
Yes, it is a hard problem to solve, but from these two anecdotes, it seems solving it in C++ is easier than solving it in C.
And yes, I know the above probably is a fallacy; gcc hackers probably spent most of their time working on CPU ports and on getting gcc to produce better code. Begin hackers, and not having a C++ code base themselves, getting good C++ error messages just wasn't important enough for them.
It took years to GCC to arrive where it is because it contained A LOT of legacy code that have been accumulating since its first versions with C++ support. GCC is a compiler that evolved little by little through long years, and many features were added with little design, to fix punctual problems, which made it difficult to converge to a clean implementation.
Clang, on the other hand, had a good deal of proper design to write a clean, robust compiler. The fact that clang reached where it is in such a short time compared with GCC is a good evidence that it is possible to write a good C++ compiler with nice performance and error reporting if you employ good engineering efforts.
No Java compiler has ever generated the kinds of error messages that every single C++ compiler did for template errors. Feel free to give me an example of Java compiler error message that takes 2 screens and fails to give you useful information.
If you compared the complexity of clang C++ front-end with complexity of any Java compiler front-end, you would see a huge difference in complexity (size of the code, time it took to develop).
The first checkin in clang code base was in 2006-06-17. It took almost 5 years and more than 31 thousand checkins to get to current state which is: not done (http://clang.llvm.org/cxx_status.html).
A fully functional, fully compliant Java compilers have been written in fraction of this time by much smaller dev teams.
The facts do not support your claim that "proper design" is enough to write fully compliant C++ front-end in a reasonable amount of time. 5 years of effort of top-notch, highly paid developers to not achieve the goal is not reasonable and it's fully the result of C++ complexity.
This code doesn't even compile?? What is the author trying to show with this example?
If I wanted an echo chamber, I'd be on Reddit.