Hacker News new | past | comments | ask | show | jobs | submit login
The Dark Side of C++ (2007) [pdf] (fefe.de)
95 points by glazskunrukitis on Feb 24, 2013 | hide | past | favorite | 52 comments



This presentation is from 2007. It's outdated and full of errors. I'll refer to the comments at /r/programming http://www.reddit.com/r/programming/comments/193o4p/the_dark...


I call BS on most of the presentation. For example, throwing exceptions from a constructor is not only acceptable, it's the only way to prevent construction.


Exactly. One valid criticism could be that getting exceptions right in C++ can be quite a challenge; i remember that at one point i was very into the language and was blown away by reading Exceptional C++ (really recommended for any C++ programmer), i was like re-discovering the whole language altogether.


Most people just miss the common C++ idioms that solve many of the exception issues in very elegant ways (and give a lot of benefits at the same time).

E.g. the example with file open + close in the presentation is laughable. Every C++ programmer knows that this is something that should be done with RAII. I.e. instead of manual close calls you let the destructor handle it. This is both exception-safe and spares you remembering to add a close call on every possible exit-path.

Also one should point out that C++11 has unique_ptr and shared_ptr which in many cases remove the need for the programmer to manage things by himself.


"I.e. instead of manual close calls you let the destructor handle it. This is both exception-safe and spares you remembering to add a close call on every possible exit-path."

Unless closing the handle causes an exception to be thrown. Either you never see the exception or your program aborts, and neither one is particularly good if you want to write reliable code.


"Unless closing the handle causes an exception to be thrown."

That is a straw-man argument. If the underlying file-closing API can throw exceptions, they must be caught from the destructor. If that means that they get ignored, then that is what it will have to be (much the same as most C programs ignore the return call from `printf`). Also in this case, the RAII wrapper should provide a member function which can execute the underlying close call early and expose the failure, for users who may be interested in guaranteed reliability.

In any case, provide RAII _safety_ does not intrinsically reduce reliability. If your destructor is executing code which _must_ succeed or be handled at a higher level, then a good developer will not put it in the destructor. No language feature can solve the question of "where should the program stop caring about failure?"


> in this case, the RAII wrapper should provide a member function which can execute the underlying close call early and expose the failure, for users who may be interested in guaranteed reliability.

Indeed; this is a useful technique. However, it means that you now have a close() method which you have to call on every exit path, because if you miss a path, that's a path which could have an exception thrown from a destructor. And that basically means you're not doing RAII anymore.

So yes, RAII is intrinsically incompatible with this kind of reliability.


> However, it means that you now have a close() method which you have to call on every exit path, because if you miss a path, that's a path which could have an exception thrown from a destructor.

Not true at all. `close` and the destructor should be idempotent. In the main path, `close` will get called and failure will propogate upward. In any other failure path, the destructor will attempt to perform the underlying close and allow the stack to unwind without any further interruption.

Of course, anyone can come up with pathological cases where this is not acceptable behavior from a high-reliability sub-component. And in such a situation, RAII may not be the best answer (nor would lexical scoping i.e. constructor/destructor, for that matter!) But in my experience, this approach is just fine for most application-level code.


...or we could retool exceptions so that destructor exceptions are safe (as long as they are caught). Conditions in Common Lisp, for example:

http://www.gigamonkeys.com/book/beyond-exception-handling-co...


Most people just miss the common C++ idioms that solve many of the exception issues in very elegant ways (and give a lot of benefits at the same time).

... and the remaining people realise that use of exceptions in C++,

1. almost always creates a trade-off in performance for strong-exception safety guarantees

2. requires special and strong control of side effects throughout the code base, even when there is minimal benefit

3. increases compile times and resource pressure

4. encourages less knowledgeable developers to inappropriately throw exceptions or make poor attempts to recover from them

5. contain some of the least portable platform semantics and handling.

C++ exceptions contain far greater costs and consequences than many programmers realise. For anything other than testing frameworks, it is debatable that they are worth any benefit at all compared to the alternatives.


While some points may be incorrect and some outdated in this 2007 presentation there are valid points. Especially it is true that the behavior of a line of code in C++ cannot easily be inferred from it alone, which can quickly become a problem as a code base grows in size and there is no common coding standard. A coding standard and automatic tools to enforce it (think clang-based compiler plugins that run before commit) can solve such problems and make C++ still one the best languages out there for high-performance programs, while guaranteeing good maintainability of the code.

There is C++ code out there which reads beautifully and is correct, you just have to work a little harder than in other languages. When you need the performance and control then it is worth the additional effort.


While I agree that ambiguous code can be a problem, I think it's much less of an issue in C++ than it is in dynamic languages like JavaScript and Ruby.

It's not at all unusual, although it is very unfortunate, to run into JavaScript or Ruby code where monkey patching will change the behavior of large sections of code several different times throughout the runtime of an app. This is especially true when the standard library is affected. Debugging situations like this can become very tedious, very quickly.


To be fair, template error messages are still a black art. They can take half a page. Reminds me of the old CFront days when C++ compiler was a front-end for another compiler. Templates are not very well integrated still.


clang has solved this problem mostly with their template type diffing. gcc has also improved in that area.

http://clang.llvm.org/diagnostics.html


Cool, but the template examples leave me unconvinced. When I get to work, I'll paste in an epic template error; hard to see how clang could make it all better :)


Hmm, I found some of the criticism unfair. E.g. the example with "baz = foo->bar(3);". Yes, "bar" could be a number of things here, but that's why variable naming conventions help.

The point about operator overloading is accepted, but this is not too different from what happens with metaprogramming in Ruby or say macros in lisp.

Even python has some quirks. See: http://stackoverflow.com/questions/1132941/least-astonishmen...


> Hmm, I found some of the criticism unfair. E.g. the example with "baz = foo->bar(3);". Yes, "bar" could be a number of things here, but that's why variable naming conventions help.

Then why must variables have static types?


What does one thing have to do with the other? Static typing reduces runtime errors by requiring you to be explicit when you want a nontrivial type conversion. There are entire classes of errors and security vulnerabilities that come from poor type checking. SQL injection is purely a result of SQL not requiring type information, which causes the SQL server to be unable to properly distinguish between the end of user-provided data and the start of the next statement, foisting that task onto the programmer who neglects it at his peril.


Static types exist for the compiler to aid the programmer in discovering errors.

Naming conventions (in c++) exist for the programmer to aid the programmer in recovering the semantics of a given expression (which the compiler already understands).


Some valid points, but I was hoping for more meat, maybe references to what I call "dark corners" of C++. Take, for instance, writing a simple logging class that can log to multiple places and can be used in place of std::cout. The solution I initially came up with is a variation on http://wordaligned.org/articles/cpp-streambufs, with some manipulator magic thrown in to adjust severity level. Ah, but that's not good enough, you have to pull some interesting gymnastics to ensure order of IOStream initialization: http://accu.org/index.php/journals/264. And after all that, I'm still not sure it works, as looking at the data structures in a debugger looks like garbage (guess why I'm in the debugger in the first place).

All that being said, I still love C++. I'm glad I know it as well as I do so that I can (usually) avoid problems, but even then I run into compiler (or build!) dependent problems that shouldn't be happening. C++ has problems, and we need to address them. I'm happy to say that even though I am stuck with old compilers that have inscrutable template error messages, time moves on and I am aware of this problem being fixed in newer versions of compilers.


C++ is a really tough language, especially when compared to more modern ones (Python, Ruby, Scala,..). There are two things which I've always found frustrating about the language. The first is that there are so many arcane elements to it that even highly experienced devs can make mem alloc mistakes that will cause the app to SIGSEGV down the road, and it's pure misery to root these out. The 2nd has to do with that it's not really cross platform in that it can be super hard to move code between win and nix, something that languages like Java and Python have all but made transparent. I know there's a lot of work done on C++ the last few years, but nowadays while we still write C++ for some parts of our app there's almost always a legacy part to it.


Another bad article against C++. There is no perfect language and C++ is not perfect too. But using some other langauges I fell offten like be prisoner...


I was exploring some features of Android this week and every other line I kept sticking comments in like "gosh, if only java had first class functions / closures / operator overloading / namespaces / global primitives that aren't hackneyed"


Haven't had one of these anti-C++ threads/rants in a while, prbably not much new here

https://news.ycombinator.com/item?id=4969372

http://news.ycombinator.com/item?id=4539251

http://news.ycombinator.com/item?id=3712292

______________

this mechanization of the lang spec in Coq looks interestng

http://hal.inria.fr/hal-00674663/en


Some valid points, some outdated ones. I have found C++11 surprisingly enjoyable. Real lambdas, templates, and unique_ptr/shared_ptr have let me write exceedingly Lispy and highly-readable code. With great performance characteristics.

Minimizing the use of OOP helps avoid the language's sharp corners. With C++11, I highly recommend abandoning the "C with classes" style.


Ugh. These C++ bashing articles, although being highly popular, are extremely grating. All languages have issues. The End.


Different languages have different kinds of issues, and these have different levels of importance for different projects and audiences.


I checked over the reddit thread mentioned in one of the comments, and a few of the comments here, and notice people mentioning how outdated the information is.

Out of intellectual curiosity, what resources (books, sites, etc.) would be recommended for modern, best practices C++?


I literally didn't know that vector<T>::operator[] doesn't do index checking for me. I've written some C++ code in Python extensions where I use vector<T> to simplify from (T* arg, int arg_length) which I now find out could randomly segfault.

Blergh. This stuff is why I use Python (with snippets of C for speed): when I do something stupid, I get an error message and line numbers, not a faceless segfault (+even when I get a C segfault, I can still get the python stack back with faulthandler..)


This a download link for a PDF. Please either don't do this or note it in the title. Not everyone wants to download a PDF to their mobile device.


Updated the title. Sorry for the inconvenience.


C++ is complicated before you know more about it.


The author also runs a blog written in C (blog.fefe.de)


And written an own Libc: http://www.fefe.de/dietlibc/


Somehow I'm not surprised.


full of crap. It sounds like when an office clerk in a publisher house demands abandoning latin words, puns, ironies figures of speech etc. because he is too dumb to use them properly. There are poets and there are mba-style bullshit writers. Great C++ code can read as beautifully as poems in style and elegance (c.f. Elements of Programming by Stepanov).


Well, the compiler error messages (few examples given by the author of the article) also looks like poetic phrases to you :D


"There is no dark side of the moon really. Matter of fact it's all dark."


Is there an article named The Dark side of mathematics?


meta: how come this didm't get scribd'd?


My bet would be that it's because the URL doesn't end in '.pdf'.


Man, never noticed how the quality of front-page HN posts on a Sunday morning really bottoms out.


That's just ridiculous. C++ has no light side.


I agree. C++ is a poorly designed language with a lot of "bugs" in it.

Languages do not pass the usual development cycle that includes collection of requirements, beta-testing and bug fixing. C++ have some obviously stupid design decisions that apparently will never be fixed.

Strangely or not, people tend to approach languages religiously rather than rationally.

Languages are like holy scripts that are written by one or a few people that are perceived like gods, and there is no proper critism or improvement following the development of a language.

Hence what we have today.

One of the most popular languages is full of stupid design mistakes that will never be fixed, they rather die with the language.

Actually, an example of a language that fixes many of the C++ problems is C#. But it inherently cannot be a replacement of C++.

Again, people are not rationally thinking creatures.

And to all who are not rationally thinking, I just say: fuck you.


You cannot determine the "best language" (for a given project) from a computer science aspect only. In the real world things like availability of programmers, existing code/interfacing with libraries, support of different platforms etc. play a much larger role.

D may be superior to C++, but if you have to build a firmware in one year and you will need 20 developers you cannot use this language since you cannot hire that many experienced developers and the D compiler may not even target your platform.

Even luck and being in the right spot at the right time by coincidence play a large part. Consider Javascript, which was never designed to do all the stuff people use it for nowadays. But for all its flaws it solved people's problems at the right time and therefore got popular. It was the same with C and C++.


C++ is not a "poorly designed language", it is a language with a number of difficult corners imposed by legacy compatibility with (originally) C, and later with its own multiple decades of use. Features that have serendipitously been free of such constraints are generally extremely well designed. It is of course possible, easy even, to design a language ex novo that has no such legacy baggage, yet none is remotely as compelling as C++ for a wide swathe of tasks, more eloquent testimony to the quality of C++ than any amount of Internet squabbling.


You call out for rationality and claim that C# fixes C++'s problems? A language which requires a heavy runtime, really?


Yea, let's write all of our higher level object juggling in C++! Such a nice language, isn't it? And there aren't much alternatives either, right? Let's be honest: if you find yourself complaining about C++ syntax and error messages while coding your higher level logic, chances are you either like the pain or you have lived under a rock for the last 15 years. In the latter case, go open your browser and type "Python" (or "JNI" or "Javascript" for that matter).


Yeah, because you can write hard real time code in Python or JavaScript.


Better stated, there are applications where you can't use JavaScript or Python due to timing requirements - software defined radios for instance.


I agree. And that's exactly why there exist interface languages such as JNI so you can write your higher level code in a nice language and get down to C/C++ for the time-critical parts. I don't see much of a problem here.


This introduces a whole new category of pains in the ass. You need to run two debuggers at the same time. Inverting control with callback interfaces is tricky. there can be friction between the memory ownership models in the managed and native code. And it just makes the system that much more complex. I've been working in a python / c++ interop system for a while and I think some of the c++11 features make the benefits pretty questionable.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: