c++11 iteration and warnings

Started by
25 comments, last by Ravyne 11 years, 1 month ago

always write the code as nicely as the language allows

I would assert that using conventional constructs to invoke hidden secret indirect subtle Klever nonmanifest behaviour is allowed by the language but could in no charitable way be described as 'nice'.

Stephen M. Webb
Professional Free Software Developer

Advertisement

It took me several read-throughs of your code to figure out that it's equivalent to this. IMHO that's bad, because it means your system is not intuitive enough, or idiomatic in terms of how iteration typically works in C++.

Thanks for the analysis, I can see you spent some effort doing it. And I agree your proposal is more readable and intuitive.

The link I used was to my fork, but it is not my system, though, just a library I am using.

[size=2]Current project: Ephenation.
[size=2]Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

The presence of 'auto' is becoming wide spread and is a really really bad practice. Because it's only good in very specific cases (templates) it's common presence in regular code makes me think you're following bad practices, which doesn't talk good about you.

In regular code, there is no benefit in using it (other than lazyness) and inherits all the problems from weakly typed languages: difficulty to grok the code for other programmers who are trying to understand what your program does or are trying to debug it. What is the variable supposed to hold? A string? an int? a float? A DOUBLE??? How do you find floating point accuracy errors when you think it's holding a double when it's actually holding a float?
A simple typo could make a gigantic difference (i.e. 1.2f vs 1.2, or '7' vs 7, "0xA" vs 0xA )* and what's worse, by using auto the coder is preventing the compiler from raising warnings (or even errors!) that would be catched instantly at compile-time if using explicit types.

Reading "MyGuiLib::TextElement* ptr = parent->getTitle()" tells a lot more than "auto *ptr = parent->getTitle()". Typing speed is not an excuse, we have intellisense for writting that. But using intellisense to understand what getX() returns while reading, is very distracting. Reading code is a lot harder than writting it. Even more if you weren't the one who wrote.
Furthermore, explicit types encourages to the one who is writting the code to be actually aware of the relations between each system (which leads to better design).

Unnecessary hindering of code is bad. In some languages it works well (i.e. Python) because they're designed to ease development are very high level, and particularly, Python makes it very hard to shoot yourself in the foot, something C++ makes very easy (harder than C though) because it's very low level.

The usage of auto is encouraged in specific scenarios, for example the cases where the creator of C++, Bjarne Stroustrup, talks about. His example is a very valid case of a good usage of auto. But abusing the keyword for everyday code? No thanks.

Makes me remember that I read in the 90's C++'s polymorphism was the new hype and some programmers wanted to make derived classes just for the sake of making them (and make every function virtual, btw) some reaching very deep parent relationships.

Just because there's a new cool feature everyone's talking about doesn't mean you have to use it.

*Edit: Just to clarify:

1.2f is a float, 1.2 is a double,

'7' is a char holding the ASCII value 0x37, 7 is an int holding the value 7,

"0xA" is a string, 0xA is a integer in hexadecimal.

Don't use auto in place of using regular type declarations, but other than that I don't think that the sky is falling quite so hard as Matias is worried about.

In general, use auto to:

  • avoid repeating yourself.
  • refer to an un-uterable type.
  • store something that really is dependent on the result of an expression.

You get yourself in trouble when you use auto to simply avoid thinking about types and their consequences.

Used appropriately, though, auto has worthwhile benefits, including a reduction of maintainance effort, simpifying template code, and reducing visual clutter.

throw table_exception("(? ???)? ? ???");

avoid repeating yourself.
refer to an un-uterable type.

Why not typedef it, like everyone else does? Same short declarations, no magic sauce to make reading difficult.

store something that really is dependent on the result of an expression.

This is really what auto is designed for. There are certain things that are very hard to efficiently compile without auto (for example, boost::spirit used to have awful trouble with recursive declarations).

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

avoid repeating yourself.
refer to an un-uterable type.

Why not typedef it, like everyone else does? Same short declarations, no magic sauce to make reading difficult.

typedefs don't avoid the repetition in the cases I'm speaking of, if the type changes, you've got to change it in multiple places. auto solves these cases. typedef is great where it works, which is most cases. I'm thinking of stuff like 'std::pair<int, int> p = std::make_pair..." -- not literal repetition, but just as useless. One could argue that this falls under the dependent type bucket, though.

And you can't typedef an un-uterable type, of course.

throw table_exception("(? ???)? ? ???");

I'm thinking of stuff like 'std::pair<int, int> p = std::make_pair..." -- not literal repetition, but just as useless.

But std::pair<int, int> p is almost as bad as auto. It conveys structure, sure, but it conveys nothing in the way of semantics. An explicit typedef std::pair<int, int> Point is infinitely preferable to either (though a struct Point {int x, int y;} would be better still).

And while I'm sure that was merely a contrived example, I think the same holds for many common uses of auto.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

While all true, "auto& x" still may cause something to be done in the iteration which may not be needed/desired. I.e. de-referencing the iterator in order to get the reference initialized. At this point you have to look at assembly to figure out if the compiler is doing what you intend (simply dropping the item and optimizing it away) or if it is actually doing unused work behind your back. As this is a relatively new language feature, who knows if the compilers are really optimizing this specific case correctly, I don't trust compilers that much and prefer to be explicit.

I'd prefer to always write the code as nicely as the language allows and if it matters, verify the compiler is doing the right thing. That may be more of an idealist approach while yours is a more practical one. Considering that range-for and auto are probably the most-used features of the new standard, I think support will be pretty good. Lars' library isn't exactly shy of using C++11 to begin with.

But the given item is "not" writing the most clean code in this case. The new "foreach" functionality is not intended for this usage, hence compilers bitch if you use it this way. I'm a clean code (I think of it as self documenting) purist in terms that I always try to write the most descriptive code possible without writing it dumb just to make it readable. My code is "usually" straight forward and where it gets complicated I add a couple comments. Using a syntactic sugar item such as the new for( : ) variation when you don't intend to use the actual form it implies, isn't clean, that's at odds with clean.

I'm sorry but I don't see how else you can argue this. You are using something for something is it not intended for, end of subject?

The presence of 'auto' is becoming wide spread and is a really really bad practice.

This is a most interesting topic, and I think we are going to see all kinds of practices of this (when to use and when not to use).

There is the recommendation not to repeat yourself, but then there is the problem for the reader of the source code to understand what is going on.

I used to favor the second view (and explictely use the type). But then I programmed Go a lot, and there is a similar mechanism. Assignments can be done as usual with "int a = f()" just as in C/C++, but there is also the syntax "a := f()" where you don't have to declare the type of a. This is used consequently everywhere, and it is recognized as a strength of Go (while still having static type testing by the compiler).

So my personal opinion is that I like to use "auto" for all but the primitive types. And I want the IDE to help me see what the type is.

Edit: spelling and wording.

[size=2]Current project: Ephenation.
[size=2]Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

The presence of 'auto' is becoming wide spread and is a really really bad practice. [...]

In regular code, there is no benefit in using it (other than lazyness) and inherits all the problems from weakly typed languages: [...]

I used to program a lot of C# and was of the same opinion regarding its auto-equivalent, the "var" keyword. Then I played around a bit with F# which completely changed my mind. F# is a strongly typed language but encourages you to not specify any types anywhere. It makes you have to write readable code without the help of type "tags" sprinkled thru out the code. It's a very good exercise. Going back to C# a completely refactored the codebase I worked with to use "var" for all variables except the "built in" types (in C# int, float, decimal, string are language specific aliases to System.Int32 etc). The code got shorter and much more readable.

Now, the type system in C++ is much more complex than C#, so I'm not advocating the same principle here. But I still think that liberal but purposeful use of "auto", when done right in the right context, can be a good coding style.

openwar - the real-time tactical war-game platform

This topic is closed to new replies.

Advertisement