Warning: ob_start(): non-static method sem_google_analytics::track_links() should not be called statically in /home/groby/codingadventures.com/wp-content/plugins/sem-google-analytics/sem-google-analytics.php on line 159

Here’s the statement in question:


Vec3 v = (2,3,0)
 

After running it, v is set to (0,0,0) – can you spot why?

In case you couldn’t, here’s what happens. C++ encounters (0,3,0) and evaluates to 0 – because comma-separated expressions evaluate left-to-right and return the rightmost expression. That’s strike one right there – the comma operator is a completely useless leftover from the days of C and gets silently applied. I’ve never seen a case where the comma operator actually was the intent of the programmer and useful in any way except to obfuscate the code.

Next strike against C++ is that I’m trying to initialize a vector here – which would have required curly braces, not parentheses. But even if I had gotten that right, there’s a good chance it fails – if Vec3 has any constructors (even empty ones), this assignment would be a syntax error.

But back to the problem at hand. The expression now boiled down to


Vec3 v = 0;
 

Now, clearly my vector class doesn’t allow alignment of a scalar. So what the heck is going on? That’s right – there is a convenience constructor that takes a single scalar and assigns it to all three components of the vector. Yes, it should have been marked as explicit. Which is yet another example of C++ defaulting to the more dangerous choice. (And really, in a code base maintained by more than one person, this will always be a problem)

Why, thank you, C++. I’ll never understand how anybody could call this language type-safe. In this simple statement, it has

  • implicitly converted a list of integers to an integer

  • promoted that (silently, of course) to a float

  • created a vector from that, again silently

At last count that’s four different types of data that have been used interchangeably. Yes, as an experienced C++ developer you find those gotchas quickly – but I’m really getting tired of having to suffer just because of an ill-conceived language. (Yes, I know why C++ does what it does here, what the design reasons were, etc. I read the ARM. It’s still a crappy result)

Commentary

  1. Mark K. wrote on 07. Jun 2008

    In general I agree with you that C++ is a horrible language, however, I think in this case you are attacking a straw man. The reason is that you almost never see examples of:

    A a = (x,y,z); // or B b = {x,y,z};

    …in real code. The reason is that C++ have been ingrained with the idea that they cannot statically initialize classes using the assignment operator. You will always see that its done with some constructor. Part of the reason for this is that C has just piss-poor support for anything other than scalar values. For example, tuples and dictionaries just don’t exist in C. Even array’s are broken since you can’t in general do:

    C c = {1,2,3};

    for a real type C that you’ve defined. It has to be a struct with no constructors, like you noted in your post.

    Fortunately, some people have heard our complaints and have made C++ marginally more useful. Look at the Boost assignment library for an example of why the comma operator is a good thing to have. Of course, any language that needs a library to help out with something as simple as assignment is probably pretty broken.

  2. dumb wrote on 07. Jun 2008

    You are an ignorant

  3. stevej wrote on 07. Jun 2008

    I agree, this is surprising. Is it a boiled down example, or did you actually write this line of code (hoping for a vector so initialized)?

    If the latter case, then perhaps you waited too long to read the ARM. This is in an uncommon usage/style/attempt (in C++), I think.

    No language that I’ve used accomodates every means of expression. Most (even C++) clobber you with a syntax error. Generally.

    Yeah, you got bit here. Try to be nice.

  4. Groby wrote on 07. Jun 2008

    Steve: This is a real example, yes. It’s not due to the fact that I waited too long with the ARM (I read it the first time in 92, I think). It’s simply due to the fact that in the environment I work in (Video games) many low level data structures are just that, plain structures. Ergo, no constructor available. It doesn’t help that every game team I’ve ever seen implements their own Vector class, with slightly different semantics ;)

    And my complaint isn’t that C++ doesn’t accomodate this. I’d be perfectly happy with a syntax error. My complaint is that by accidentally typing something that’s clearly not valid code, all the “magic” imbued in C++ generates working code that’s not even close to what I intended.

    The main complaint: explicit constructors are safer than implicit ones – they should be the default. (Immediately followed by the comma operator)

    Mark: See above – we’ve got plenty of PODs, so the initialization code is unfortunately a valid choice. (There are also plenty of constructors. But as long as I’d get an error if I picked the wrong one, I’d be happy)

    Dumb: Always glad to see a well-reasoned argument.

  5. Tom Ritchford wrote on 08. Jun 2008

    “I’ve never seen a case where the comma operator actually was the intent of the programmer and useful in any way except to obfuscate the code.”

    What about this super-common idiom:

    for (int i = 0, size = v->size(); i < size; ++i)

    ?

    (you might not want to use an iterator, perhaps because you want the index as well as the value…)

  6. Michael Mrozek wrote on 08. Jun 2008

    From now on I will be insulting people with “You are an ignorant” :D . And while the example does seem silly since the assignment operator is being misused, I agree that C++ is very prone to subtle errors, which can be frustrating at times.

  7. Groby wrote on 08. Jun 2008

    Tom: OK, that is a fairly common case. I’d argue it’s unnecessary, but that’s mostly a stylistic question, so let’s not even go there…

    Michael: I’m a bit disappointed the first flame I got on this blog was that weak. Ah, where are the good old days of comp.lang.c++ where people knew how to flame ;)

    And the most frustrating bit of C++ are not even the possibility of subtle errors – compile times are horrenduous. (Expect to see a few more articles on that)

    In general: in case anybody thinks this is my only beef with C++ – it’s reason #947, as I said. It’s just the one thing that caused me to vent.

  8. Patrick Geiller wrote on 02. Jul 2008

    There is some direct initiliazation code in Quake, stuff like Vector = {0, 0, 1}. Vector is defined as float[3], though.

    As for the comma operator, one use in Javascript :

    function doStuffWithNode(node) { if (someInvalidCondition) return Warning(’invalid node’), null }

    This would call the log code then return null. I really prefer that to opening up braces and using 4 lines to log then return.

  9. Thomas BOUTON wrote on 02. Apr 2009

    Just found this post today, I’m just one year too late. Obviously, you are not using correct tools:

    $ cat test.cpp struct Vec3 { float i1,i2,i3; Vec3(float toto); };

    Vec3 v = (2,3,0); tom@tomdell:~ $ g++ -c -Wall test.cpp test.cpp:6: warning: left-hand operand of comma has no effect test.cpp:6: warning: right-hand operand of comma has no effect

  10. Chris wrote on 13. Apr 2009

    @ Thomas BOUTON:

    I just read this post too and I was going to say the same thing! There are flags built in to stop you doing this sort of thing. I like -Wall -Werror -Wextra -pedantic off the top of my head, there are at least 5 that should be a requirement for any project, I have a standard line of about 10 flags that I apply to all of my projects.

    http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

    @ everyone:

    I know if you are in bussiness or games programming you might not have a choice but to use Visual Studio, but at least compile under gcc once in a while, even if you don’t release with it, just to make sure that you are creating robust code. At my work place we have 3 automatic regression/build test machines that every hour checks out a copy of our code and tries to build the Visual Studio project and gcc project for Windows as well as the gcc project for Linux and Mac and then email us if anything fails to build. The best thing is that each developer doesn’t need to actually have a Windows, Linux and Mac machine to write cross platform code, he quickly gets an idea via email of what will not compile. It would be great in the future to also have a commit hook so that you can’t even commit if the code is not cross platform.

Leave a reply