stop crapping on C++

I happened upon this article, about the double-casting anti-pattern in C#, and I couldn’t help but notice the little pot-shots taken at C++ (and Delphi), to the point that people who exhibit use of this anti-pattern probably came from those weakly-typed languages.

First off, this article is weak. Laying the blame at the feet of C++ is rather unrealistic, especially since the author manages to never show what he thinks is the C++ equivalent. I’m sure you can imagine the equivalent double-casting inefficiency in C++, but what the author doesn’t show you is that C++ has a way to handle this problem in the exact same way as C#, except a little bit better.

The C++ code equivalent of the C# pattern (using as instead of is), is:


if ( Foo *foo = dynamic_cast<Foo*>(someUnknown) )

That’s right, C++ supports the exact same operation, which is effectively: “Try to cast this pointer/reference of some type to another type, returning null if the cast fails.” It looks clean, you know exactly what the lifetime of the new Foo pointer is (as in all languages, it is best to limit the scope of the variable to as small a range as is appropriate; in this case, you only use it if it is valid).

Let’s revisit the C# example from the aforementioned post:


public static int SingleCast(object obj) {
    Foo foo = obj as Foo;
    if (foo != null)
        return foo.Length;
    return -1;
}

Anyone notice any issues? There are two major ones, and they will come back and bite you in the ass.

First off, the Foo object lives for the entire length of the function. If you make a silly mistake and reference foo outside of the if statement, what you are doing is quite semantically valid. The C++ example makes sure this isn’t possible, but C# certainly drops the ball here (this is part of the larger debate about making assignments in if statements).

The second thing is closely related: what if I need to make multiple checks? There are really two options:

  1. Do all of your as casts up front, with a different variable name for each one, each of which suffers the problem stated above (lifespan too long), each of which pays the price of trying to do the cast. Now you’ve polluted your function with n variables, of which n – 1 are guaranteed to be invalid.
  2. Do one cast at a time, nesting each subsequent cast in the else block of each last if statement. This has the benefit of avoiding the penalties of casting each and polluting too much of the function (at the end of each layer i, you have n – i – 1 invalid variables). This has the problem of being rather ugly and unreadable, as three casts, for example, will require three levels of indentation. If you are already a few levels in (class -> method, at least), then you are talking about at least five levels of indent, as opposed to the flat one level. Besides, it doesn’t make sense to have these things be nested, as they really all take place in the same environment.

So, with #1 we lose efficiency (which was the whole point of doing as instead of if) and pollute the namespace with a whole lot of null references (and its ugly, I should add). With #2, we get the efficiency back, but now we’ve created supremely ugly code.

There are plenty of things that C++ lacks and reasons why you or a company might prefer C# to C++, but, in this area, C++ kicks the crap out of C#.

On a more general note to all of the computer “scientist” hacks out there, stop complaining about things you are too mentally challenged to comprehend. Stop villifying languages like C++ because you could never figure it out, and stop touting C# or Java like they are anything more than languages designed to make idiot programmers like you into passable programmers like any other Java or .NET development house. Just go away and be happy that you figured out how to breathe this morning.

  • gcc tends to crap on that general type of thing ("did you mean '=='?"), but you can remove the warning by putting an extra set of parens around the expression. So this:


    if(Foo* foo = dynamic_cast<Foo*>(someobject))


    becomes:


    if((Foo* foo = dynamic_cast<Foo*>(someobject)))


    It might look a little silly, but I think it is quite clever. The warning itself even tells you, "warning: suggest parenthesis around assignment of a truth value", so that you know how to suppress the warning without using any special #pragma type labels. Nifty, eh?

  • I was initially concerned that at really anal warning levels, any use of your suggested pattern would get flagged ("Did you mean '=='?"), but I was pleasantly surprised to find that the MSVC compiler is trained to suppress warning that would result from assigning the result of a dynamic_cast inside a conditional.
blog comments powered by Disqus