More Defective C++ ;)

Warning! Some information on this page is older than 5 years now. I keep it for reference, but it probably doesn't reflect my current knowledge and beliefs.

May 2010

Today I'll disclose my opinions more than ever before, inspired by the C++ FQA Lite (Frequently Questioned Answers) - a great reading I recommend to any C++ programmer. If it's too long for you, just be sure to overview the introductory chapters - "Defective C++" and "Big Picture Issues". This document is written by Yossi Kreinin - a man who apparently knows and uses C++ language although doesn't like it - just like me. I like his text so much because I agree with most of his arguments. The main part of the FQA has a form of alternative answers to the questions from the C++ FAQ Lite. It's very subjective, emotional and also funny to read. I've especially laughed at point 23.2 - it's so true about all these design patterns :) Surely my opinions expressed here are not so valuable compared to opinions of all these older and more experienced programmers around, but that's what blogs are for - expressing one's opinions...

My list of objections about C++ language is long. Some specific ones are:

1. Lack of binary compatibility. You cannot compile a C++ library into binary form as LIB or DLL, distribute it and be sure people can use it with different C++ compilers. It works with C, while C++ libraries have to be either distributed in source form and compiled by the user in his compiler or distributed as LIB files just for particular compiler types and versions (like Visual C++ 2008).

2. Lack of "finally" keyword - you know, the keyword to be used with "try" to execute some cleanup code no matter if an exception was thrown. Many programming languages, like C# or Delphi, have it despite they don't need it so much. For example, in C# memory is automatically freed by the garbage collector and for other resources we have very convenient "using" construct. Whereas in C++ it would be very useful when dealing with exception and we don't have it. I know, we should use RAII idea instead, but how many of us create RAII wrappers for all kinds of resources we deal with in our code, like FILE* or IDirect3DTexture9* ?

3. Lack of properties - the construct for classes that work like get/set methods while can be used like fields. It's very convenient and present in many programming languages, except C++. But it's not such a big deal anyway, comparing to other issues.

4. Lack of pointers to member functions. This one is very important. And it's not only an exotic feature of very high-level scripting languages. C# has it, Delphi has it. I'm talking about the type of pointers that point to any but particular method in a particular object of any class that just have compatible argument list and return type. Its most clear application is a GUI system, where MyApplicationWindow class should define some methods like OnCloseButtonClick, OnAboutMenuItemClick, and tell the main menu and the button object to call them from the object of my window class when the command is issued by the user. And so every GUI library for C++ implements its own such mechanism, whether call it delegates, events, pointers to members or signals and slots. wxWidgets does it with some macros just like MFC, while Qt requires its own C++ code preprocessing step to do this. There are also some free, standalone and lightweight libraries for this, like FastDelegate by by Don Clugston or the The Impossibly Fast C++ Delegates by Sergey Ryazanov. Pointers to member functions available in C++ are quite useless - they point only to a member of particular class and are not connected to particular object.

5. Casts - the new C++ casts are overly long. dynamic_cast is OK, very useful as a part of the RTTI, but I can see no point in using reinterpret_cast, static_cast and const_cast. Just as I proved in my article Rzutowanie typów w C++ [pl], it can do nothing more than to old good (Type*)value C-style cast while being much more verbose. With static_cast(floatNum) you do numeric conversion, but it does not mean reinterpret_cast(floatNum) will bitwise reinterpret the value. It just won't compile. And no, their verbosity is not an argument to me. I'm not going to search my whole project for "reinterpret_cast".

6. Atomic types have no explicit size defined, so the standard doesn't say the int is 32 bit and the short is 16 bit. Even worse, it doesn't say the char is signed or unsigned. Unfortunately it's signed in my compiler, so the Polish diacritic 'ć' is not greater than 'A' in numeric manner, opposite to what ASCII table says. By the way, why treat character, bool and enum types like numeric types without requiring explicit cast? I though C++ claims to be a high-level language. Well, I'm glad is has a bool type anyway, opposite to the old versions of C.

But it's not such specific issues what makes the C++ language so problematic. It's a general ideology behind it, the mindset of its creators. For example, the whole concept of header files causes much trouble. Other programming languages work well without it, while in C++ this preprocessor-based parsing of hundreds of kilobytes of declarations in each and every CPP file has following consequences:

About the templates, function overloading, operator overloading, I'm not totally against it. It's very powerful and useful in some situations. But I don't like the idea of abusing these language features to achieve some simple things with overly sophisticated code, just like they do with STL algorithms, iterators, functors and the stuff like that. If you saw Alexandrescu book, you know what I mean.

I blame C++ for not standardizing the things that should be standard in any high level language. One could argue if string and containers should be part of the language syntax and not just classes coded in this language, but that's different story. What's more important is that the standard things provided with C++ - STL strings, containers, exceptions - are not commonly used by C++ programmers. Every bigger C++ library - whether it's a GUI framework (like MFC, wxWidgets, Qt) or a game engine implements its own string, containers and the way of error reporting. Which is an EPIC FAIL in my opinion :)

Another general problem is the slow (if any) evolution of the language. The slowness and conservativeness is a problem of all technologies managed by some committees instead of a robust company that also makes the implementation. Let's consider the "auto" keyword. It's going to be introduces in the new C++ standard, not yet released in a final version. Meanwhile Microsoft already defined and implemented it as "var" in C# version 3.0. And don't tell me C# is a proprietary language created by the evil company - it formally is an ISO standard :)

One of the strange things about the language designers is the unreasonable fear of introducing new keywords to the language. That's why we have all these new strange constructs like && or the syntax for lambdas. Have you seen recent C++ joke? Here it is: [](){}() It's better to know what it means because language lawyers love such puzzles so some day on some interview it can help you to get a job :) It's actually an empty lambda function. Beautiful, isn't it? :) I can't imagine why are they so afraid of introducing new keywords. Do they worry that some legacy code will stop compiling under new compiler (why recompile an old code if we are not ready to modify it and why to use a new compiler for it) and companies will have to hire specialists and pay them millions of $$$ just to do simple Search&Replace?

My another objection about C++ is poor compiler optimization. OK, I know that sounds radical but I dream I could just return my vector or matrix type by value and be sure the compiler will optimize it to the code as fast as manually filling the output structure passed as an argument by the reference or pointer. OK, my vector has non-trivial constructor but it only rewrites its arguments to the x,y,z,w fields. Is it really so hard for the compiler to inline it and then optimize the entire function code? It seems that C++ compilers are only good at optimizing C subset. That sucks. Despite the age of C++ language there is still so much to do for the compiler programmers...

Crypic error messages. Really, I haven't seen in my life a compiler for any programming language that would print error messages so unrelated to the real cause of the problem as C++ compilers, whether it is Visual C++ or GCC. There is even a tool to decrypt the long compiler error messages - STLFilt which is funny by itself, but C++ would need much more comprehensive tool to make its error messages relevant. For example, a translator that would replace "missing ';' before identifier 'variable'" with "Unknown type of the 'variable'".

Going back to the C++ FQA document, I like most of the opinions expressed there. I agree the code should be simple and readable rather than clever or theoretically pure and correct. That's why I dislike singletons and prefer explicit initialization and finalization. Same applies to non-trivial constructors - initialization methods are often better. I like this quote:

Abusing C++ syntax in order to cover up deficiencies of C++ syntax, thus creating real problems in attempts to solve non-problems, is a popular hobby among C++ professionals.

I also agree with the author about his opinion on object-oriented programming. Especially:

The document is also quite compliant with the data oriented programming paradigm, so popular among today's game developers. Quote:

Data representation is very important. Too bad so many people believe that the only important thing about data is to hide it behind code (...)

About the chapter "Inheritance -- proper inheritance and substitutability": Several points talk about whether a Circle class should derive from an Ellipse class or the opposite. Arguments appear like "But I have a Ph.D. in Mathematics, and I'm sure a Circle is a kind of an Ellipse!" The FQA does quite a good job explaining the correct mindset to resolve this dilemma, but in my opinion one could go further and look at this problem from the data-oriented point of view. What data should the Circle class contain? Probably: vec2 CenterPosition; float Radius; Now, what data should the Ellipse class contain? That would be: vec2 CenterPosition; vec2 Radii; Now the correct design becomes clear: we should define an abstract base class Shape with vec2 CenterPosition; field, next Circle and Ellipse subclasses, each having its own way of defining size.

I also agree with an unpopular belief the FQA author also claim that it's often better to just document something in a comment, like /* Please don't inherit from this class. */ or /* Please don't create objects of this class on the stack. */ rather than to do some strange language tricks to prevent other programmers from doing it.

Here are some places where I don't agree with the FQA author. I don't agree C is better than C++. To be honest, I've never coded in pure C and I can see no purpose in doing so. I simply can't consider a language with no classes, no built-in strings and containers a high-level language suitable for coding any program bigger than a console tool for processing some binary data. Sorry, I'm simply too young to remember the times when people tried to code big things in assembler. And even if I was, we all agree that computer science evolved since that time, software became more complicated and so we need more high-level technologies to keep up.

On the other hand, I don't agree that C++ should have garbage collection. GC is a controversial topic so I'm not so sure about it. If the gamedev programmers that care so much about performance are so afraid of dynamic memory allocation, maybe GC would help even them? Allocations in managed languages are faster AFAIK. But it's obvious that GC causes some significant and unpredictable performance as well as memory overhead, so it's not suitable for many applications where C++ is used.

I also don't foster the author's opinion about all these sophisticated language features and unclear rules like the function overloading, operator overloading, conversion operators etc. I believe they may be useful sometimes although they can also be abused. It's all the matter of how you use them. That's why I care more about the features that C++ lack than the features C++ has while being not so necessary.

Finally, I don't agree with FQA document that it's usually better to use either C or some managed language instead of C++. I believe C++ is a kind of a compromise - low level enough to be able to process binary data with no unnecessary performance overhead while high level enough to be used for writing big systems. That's why it's probably the best choice in game development and some other applications. But that doesn't justify all the flaws of this language :P

Comments | #c++ Share


[Download] [Dropbox] [pub] [Mirror] [Privacy policy]
Copyright © 2004-2020