Sunday, October 3, 2010

C++ Yuckiness...

Following up my dissection of Java ages ago, here's one on C++ (as promised to Moguri a few days after that post a few months ago ;)).

In short, what's wrong with this language: lots.



1) Templates - these obfuscuate and screw up many code definitions. They also result in unreadable warnings where the interesting stuff is in the last line of 3-page prints.

2) "Reference" variables - I don't know what Mr. S was smoking when he designed this part of C++, but IMO, these are a complete and utter POS. What's wrong with just using straightforward pointers that clearly show in the code what's going on! But no... C++ must provide a bastard-child third type of variable that acts like a dereferenced const pointer using standard variable syntax. Worst of all, the C++ means that you MUST use them if you want to do any operator overloading. This really sucks, as the operator overloading would have otherwise been one of the nice features of C++.

3) String library - as a result of 1) and an overambitious design which tried to tackle bloody Unicode crap at the same time, the standard string library in C++ is complete and utter rubbish. None of the methods are nice to work with at all. In fact, none of them make any/much sense for normal usage.

C's "strings" (i.e. null-terminated char arrays) were way better than this. Heck, even Java's string library has a leg up on this rubbish. But, my personal favorite for string manipulation has still got to be Python (P.S. I LOVE the [-1] indexing and "slices" stuff... pure genius!).

4) Constructor invocation on declaration - for inline or stack-bound structs/classes, their constructors get called when their definitions are hit. But yet, 'normal' variables are not initialised to zero by default by the same stroke of "logic", as their default-constructors would have to do something, and what better than to use zero for anything we don't know about.

The main problem of this approach though, is that many constructors out there "do stuff" rather than just zeroing out a block of memory. Hence, they will "take time" whenever they're run. So, people don't want to have a bunch of constructors running at once before anything else happens. Therefore, we get the mess that is declarations all over the code vs in a tidy pile at the start of each block. IMO, variable declarations should be at the start of each block/scope (NOTE: that does not imply start of functions only!) that they are useful for, since only that way can we have any consistent way of finding them.

5) Cannot chain constructors - that is, one constructor cannot call another constructor of the same class. At least that's what I remember from when I last had to use this ramshackle set of hacks. This makes some things a lot less convenient!

6) Initialisation lists - these are a list of function-call like statements following a colon after the constructor definition. This spawns from 4) in a way. IMO, this is quite disgusting: it divides up your constructor code into "stuff that is simple assignment/value-setting" and "stuff that needs more trickery", while acting as one of the complications for 5).

I've heard the argument that these may be "faster" than putting everything in the constructor body, as you avoid the copy operations. However, if that is really the case, then shouldn't effort have been spent on finding a way to optimise the generated code for those cases "in the backend" instead? Sure it may be difficult, but these are after all tools for making humans more productive, so we should be trying to make our lives easier with suitable tools ;)

7) Namespaces - I'm not totally sure whether I like them or not, but I err on the side of "get outta my way, I don't want to type out long paths everytime to access things". Once again, they stuffed up what happens with "standard libraries" in relation to this, which makes them more nuisance quality than anything.

8) Layout of most of the code out there - this is not really so much the fault of the language as the developers using it, but I still feel that some responsibility must be shouldered by C++.

As my experiences working with Bullet has further solidifed, I've come to the conclusion that the ability to "define functions in header files in the class definitions vs in the cpp files with the rest of the code" is an absolute minefield for navigability of source code. You never know where to find the code for some function to verify how it works, as it could be in one of two files. It also means that you don't have a file with a clean list of the prototypes/definitions/public-API for a class, as there could be some implementation scattered amongst that.

For all the shortcomings of C++, IMO this is the one thing that is avoidable. It is something that all C++ programmers can fix in their code to improve it. To a certain degree, I suspect this is one of the major reasons why some people have the belief that "code is a bad representation of a system for understanding it", and thus go out promoting UML as the magic-bullet.

9) Private methods for classes cannot be declared by just declaring them in the namespace of the class (as part of the code) without adding this declaration to the class definition itself. I don't believe that all private methods belong in this category, but at the same time, not all private methods are created equal!

Some private methods perform "big roles" within the class, and/or are quite dangerous to play with, so they are hidden away as private while some more padded/"safer" public methods are exposed as part of the classes' public API. These are the ones that should be declared within the namespace of the class for sure.

Then there are the private methods which are called by several methods in the class. Again, since they're quite important (they form part of the 'private' or internal API of the class) they should be included in the class definition, so that it's clear to all what their role is.

However, there exists a third class of private method: a "helper" function to some other method which is only called for one particular method in the class. Nothing else needs to see it, and this function is really of an auxiliary nature anyway. IMO, it's really not worth adding this to the classes' "internal API". There just isn't any real point.

10) Overrideable methods need to be marked as such in the superclass. It seems C++ is built on the premise that overriding stuff is something that you only very rarely want to do. That, and making you type out a lot more code...



This is only a short list of the things I can still remember of C++'s yuckiness. The rest I've (fortunately?) happily forgotten ;)

In practice, C + Python (and Haskell - for anything mathematical where accuracy counts, but not being able to do it efficiently or in any particular order) do me just fine thanks. :)

2 comments:

  1. I had wrote a very long comment, but you blog system eat it whin I pressed submit...

    So, to summerize, I agree with you !

    ReplyDelete
  2. [quote=CorsairX]
    Hi Aligorith,
    Just responding to your recent blog post here as I don't have any of the ID profiles on your blog.

    I'm also none to fond of C++'s string library, particularly after the joy (mostly, ask me about Pythons little Unicode tricks on a bad day) that is Pythons. While occasionally a little awkward, I've found the pystrings library does a fairly nice job of bringing Pythons common-sense over!

    http://code.google.com/p/pystring/
    [/quote]

    ReplyDelete