2008/02/02

C vs. C++

Some time ago, Linus Torvalds have written some comments about the C++ language. It's hard to disagree with Linus in the point that C++ is a horrible language, but I find much harder to agree that C is a better language than C++ - even counting some "non-technical advantages" cited in the article.

C++ language is about structures and abstraction. Big projects, like GNU or Linux, tend to recreate that kind of structure over C when good programming techniques are involved, facing things like generic containers which cannot be type checked and inhability to use some very useful paradigms - just to start naming a few disadvantages. Reducing the benefits of the language to things like "nice object-oriented libraries" or the design of "object model crap" is just buying OO gook - which does not stand for C++ as what it can be, but for what it is when used by Java programmers. You can dislike virtually any language by bad programs and bad programmers which use it - even C - so this should not count when considering the merits of a language.

Talking about the real advantages of C++ over C, some essential points come to my mind:

  • You can define behaviours inside structures (or classes, if you prefer the sound of the name), and encaplusate what data will be accessible and what data will be opaque to the rest of the program. This allows you to define libraries for each needed feature/resource of your program (converting text encodings, manipulating strings, processing config files, etc) and combine them easily, reusing the code as needed. You can even use those advantages without objects, defining structures with only static methods;

  • Memory management is something you will never leave you wake at night again, thanks to references. You can always assume that a reference would exist and be initialized, even a reference being essentially a pointer. That gives you the advantages of passing mutable values to functions, without needing to know where you would need to deallocate them. Manual memory allocation/deallocation with new/delete can be so scarce that you will count in your fingers how many manual allocations you will need to have in your program - even for projects with size bigger than 20.000 LOC.

The critics used by Linus are actually very interesting, as they fit C as a glove. Let's talk about each one individually:

> infinite amounts of pain when they don't work (and anybody who tells me that STL and especially Boost are stable and portable is just so full of BS that it's not even funny)

This is actually real, STL and Boost give absurd template errors when you make coding mistakes, and they also may throw unwanted runtime exceptions if you use something incorrectly. The only real solution for the first is to fix the language (maybe 10 years from now) or to gain experience with C++ -- when you are a experienced C++ programmer, you learn to deduce what is wrong in your code based on the error messages. The second may be solved by documentation, or by searching at the library source code for "throw" clauses.

These two problems are bad design decisions made when the language got those features - unfortunatelly, they still remain. But the main point here - or, being more dramatic, the real important point - is that the first problem (template error messages) is a compile-time error, while the second (if you consider checked exceptions) can be solved at compile time.

In C, when your program does not work, the OS generates a nice "Segmentation fault" in the screen, which leaves you totally clueless, and with infinite piles of pain. I prefer that my programs just compile if they are right in the sense of "not crashing", and that the compiler - not the user - should verify that. The funniest thing is that you may have the same problems of C in C++, but only because you can program in C while using C++.

Finally, in my little experience of portability, I used both STL and Boost mainly in two operational systems (a little time in Windows XP, and the rest in tons of GNU/Linux distributions), and never had ANY problem compiling code in any of those systems, even using different compilers and compiler versions. Probably C++ compilers have evolved in the mean time, as they got mature projects.

> inefficient abstracted programming models where two years down the road you notice that some abstraction wasn't very efficient, but now all your code depends on all the nice object models around it, and you cannot fix it without rewriting your app.

Sorry, but this argument is senseless: you can abstract anything incorretly in ANY language. And as far as my experience of programming goes, the pain of rewritting and application is much more severe in C than in any other language that can be called "high-level".


Other common argument against C++ that I hear often (and that I've already used, while programming in an embedded environment), is that it is not as efficient as C - mostly because of the abstractions involved in the langage libraries. That is a fact, actually: conventional C++ libraries usually are bigger than writing your own set of C function for dealing with your problem, and memory consumption will be much higher than allocating everything manually and statically.

However, it should be noted that good abstractions need good compilers, which can do good code optimizations. Recent GNU C++ compilers showed how a compiler can affect a language performance when new optimizations algorithms started being used. It is also important to mention that if you have a library written in C++, and you want to write the same thing in C, it would be bigger to codify, harder to maintain, and - specially if the library is some kind of container - unable to be checked for safety in the same extent C++ can.

For embedded environments, or things that need critical per-instruction optimization, the best choice is always C or assembly: you can garantee the compiler won't get in your way, neither the code will run in an undefined time length. But for application programming, the only excuse for not using C++ is using a better language - whose group, C does NOT belong.

And that's it.