GCC 6: -Wmisleading-indentation vs. “goto fail;”
developerblog.redhat.com
developerblog.redhat.com
I'm glad Python (with semantic whitespace) and Go (with gofmt) solve this problem.
if (ptr) call_oldschool_thingy(ptr); if (image) foo_release(image);
if (label) foo_release(label);
if (data) foo_buffer_destroy(data);
if (window) foo_window_destroy(window);
It's easier to see that all objects are being cleaned up when each occupies just one line, as that typically matches the look of the initialization: window = foo_window_create();
data = foo_buffer_create(1, 2, 3);
label = foo_label_create(window);
image = foo_image_create(window);The second example is missing error checking. So the real code isn't that nice.
My point is that C shouldn't look like Python. Small amount of functionality should be written unambiguously and take a lot of space if necessary. Because of the nature of C, it needs a lot boilerplate, and will take a lot of screen space anyway, but that is not a problem, as we are not coding on paper.
image ? foo_release(image) : 0;Any C programmer will recognize what (void)0 means, do nothing.
I find the single line "if" statement without braces to be clearer and simpler than ternary with a (void) expression, and don't think the downsides are significant. It breaks if you were to add another statement after the semicolon on the same line, but I think that should almost always be avoided anyway.
I do find it interesting that others prefer two-line without braces over the single line approach. I find this one to be more dangerous than the single line. Possibly because with line-oriented debuggers it can be hard to set the right breakpoint?
It's quite possible that others are downvoting because they think you are trolling, and that no one would actually believe the ternary operator to be clearer. I wondered also about your defense of Allman braces, which I didn't downvote because I think it's a good example of how different the others's views can be on what seems obvious. While I think (some of) your views are in the (very small) minority, please keep posting them!
if (ptr) { call_oldschool_thingy(ptr); }IMHO there should be a -W to enforce this so those of us with -Werror on can catch it and burn it with fire.
Here at Google the style guide requires block-parens always.
I got used to this quite a bit and these days if I write code outside the project I work on, I d sometimes omit the block-parens, but I always add extra indentation to ensure the condition is visually well-separated, e.g.:
if (condition1 || condition2) execute_foo1(with_bar);
if (condition3) exectute_foo2(); if ptr { call_oldschool_thingy(ptr) }
(Keystroke count isn't an important metric for me, but apparently very important to some people.)Now if only Rust supported the elision of ; ... (Yeah, I know that they have a reason to not do that, but I don't buy it.)
Meeting snippets of code like that when debugging is a constant source of frustration for me, because I have to stop what I was doing, edit the code, rebuild, then get back to where I was. Particularly galling when the code in question is in a commonly-included header, and the subsequent build takes several minutes, and I was on a particularly productive-looking trail.
(The last project I worked on solved (?) this problem by performing so shamefully poorly in an unoptimised build that it effectively didn't work. So you had to debug the optimised build. And so single-stepping at the source level just didn't work properly anyway.)
>I've never met a C or C++ debugger that handled this case nicely, unfortunately.
Apparently you never use Visual Studio. It can highlight a portion of the statement on each step (at least it used to)In C++ optimized builds, you might be jumping back and forth between lines due to the compiler re-ordering instructions, and there isn't an easy way to get back code that's in-order.
In the sense that a programming language is not only made to be parsed by a computer, but also read back by a human.
If we ever move beyond using text files for storing code, we could eliminate formatting differences entirely... but efforts in that direction have run into many issues in the past.
gofmt is still optional. Python significant white spaces are not.
This is not the case.
>I'm glad Python (with semantic whitespace)... solve this problem.
Python solved it....then added the problem of making things you can't even see semantically significant. If you write Python, it's helpful to have an editor that makes the difference between tabs and spaces visible....I do this for any and every language. Mixing tabs and spaces always sucks.
Please. I'm as big a C apologist as you'll find and even I can think of six or nine thing objectively worse about the language.
This issue is merely a great bike shed because it allows all of us rabble to join in on a discussion about "compiler" technology.
Look: it's a good warning. People should clearly use it. It probably should have been written long ago, and probably would have if our editors hadn't been enforcing this since time immemorial.
But optional braces are perhaps the most pointless anti-feature in C. The only benefit is a minor (and subjective) improvement in aesthetics.
Sometimes you can write code that looks "prettier", at the expensive of the ability to read and reason about it. Optional braces are frequently used for this effect. Yes, optional braces allow you to fit more code on the screen with less noise—and therefore could be thought to enhance some kind of readability. But at the same time, they require you to think harder about the grouping of the code around them. Unless you've got a reformatting linter, you could have code like this:
if(foo)
bar; baz;
quux;
and optional braces require an extra subroutine to be constantly running in your head, looking for those mis-arranged `baz`es that will get executed anyway. That cognitive load detracts from your ability to read and reason about the code.(What'd be really interesting, in my opinion, would be to make C's braceless blocks, and only braceless blocks, have semantic-whitespace. Then the above would be able to be easily reasoned through: `baz` is part of the conditional, because it's on the same line. But this breaks a lot of rather old and inflexible assumptions about how C is parsed, that macro-writers et al depend on.)
if (a)
...
else ...
But for some reason I find the below _very_ frustrating. It feels misleading and I find it to be very, very ugly.if (a)
...
else { /* Multi line block */
}I've seen
if (a) thing;
but never with an else, and certainly never with an else that has a multi-line block. That's definitely somethign I'd call out in a CR.
I'd rather a compiler forces me to put braces around everything than let people have the opportunity to do something like the latter from my original comment.
The else is executed if there is no error, in my style at least, so naturally it will contain more logic.
This is also one of the reasons I like Racket: there's no such thing as ambiguous block delimitation. Also, everything-is-an-expression is very useful.
The scheme works so well that people will often go months into their Haskell education before they encounter code with explicit braces and semicolons.
And mandatory seat belt use is foolish, bike helmets are for wimps, and blade guards on saws are a waste of space and money.
Just use those tools responsibly :P
What's frustrating is that K&R still has a lot of example code like that as well. It would be wonderful to see a 3rd edition even if it do nothing but change its code to use braces.
You need indentation for humans to understand the structure. Why do you also need braces for the parser to understand the structure when the parser can use the same information that your eyes use? You therefore avoid the possibility of the two signals contradicting each other.
Also, a closing brace is a nice signal to the editor that it's time to "outdent".
Finally, people seem to forget that python already has an opening brace, except it's spelled ':'. So why no closing brace to match? This seems inconsistent to me.
Your other points could be debated but really, is coding so keyboard-limited that saving a single keystroke is a major issue? Python has focused on comprehensibility, which I think is the right balance given how frequently people need to understand code versus write it.
I switch between C and Python quite frequently. With C, I just type the code, and let my editor manage the indentation, which it can do perfectly without any help from me. With Python, I find that I spend a lot more time thinking about the formatting itself, especially at the end of block, because it's up to me to get it right.
I fail to see how the _lack_ of a closing brace _improves_ comprehensibility. What Python has done is removed a helpful indicator, and put more of the responsibility on the programmer.
I'm happy to type that extra character in C, because it helps lower my cognitive workload so I can focus more on the problem I'm trying to solve. The closing brace doesn't mean anarchy, or make the code harder to understand later.
1. Move the cursor to wherever you want to put the block 2. Hit paste 3. Your editor adjusts the indentation so the entire pasted block is inserted as if you just keyed it in
It's simple and reliable and as a bonus it's portable across any language where the code is indented correctly.
> The closing brace doesn't mean anarchy, or make the code harder to understand later.
It's true that well-written C code can be almost exactly the same but the difference is that the world is full of sloppy C code where someone hammered out a bunch of changes, decided it was too much work to format it so the visual display matched the actual parsed structure, and left a trap for the next developer who touches that code. Yes, hopefully they'll review & test carefully but, as with e.g. memory management, we have decades of proof that depending on programmers to consistently follow desirable practice is a losing battle unless it's enforced by tools.
The point isn't that braces are bad and that whitespace is good but that Python will refuse to execute one class of sloppy code. Imagine if GCC made this flag mandatory or Clang refused to compile anything which didn't pass cleanly through clang-tidy – the benefit wouldn't be due to the braces but from the fact that one category of error would simply no longer be possible and every C developer on the planet would spend less time on cosmetic differences when reading other people's code.
But... we could have had both! Absolutely require the formatting, but also keep the magic symbols that help your tools help you!
I'm on the side of having to reflexively type an extra character or two to gain some parser redundancy and error detection. I've also read a few people talking about auto formatting tools and seems like they need all the help they can get.
I'd clarify that to "lambdas can't contain statements". Lambdas can contain expressions, which can be nested arbitrarily, have side-effects (e.g. tuples guarantee left-to-right evaluation of elements), and be spread across multiple lines. It's not exactly pretty though ;)
I wrote about this a few years ago at http://programmers.stackexchange.com/questions/99243/why-doe... and slightly more obtusely at http://chriswarbo.net/blog/2012-11-17-anonymous_closures_in_...
Omitting braces in this case leads to a lot of problems.
Using braces everywhere is definitely good style, but it isn't a practical solution because there's a ton of existing C/C++ code that doesn't use braces.
It would make sure that no new errors are introduced due to this.
The compiler would need to require braces in new code, but allow brace-free style in old code; you'd need to integrate it with the source control system so it knows what code is new. Doable, but tricky.
- all new files are checked
- old files are put in a whitelist for the style checker, the submitter is supposed to remove the file from the whitelist when signifcant changes are made
It is good to hear others are doing it.
It is never an error to insert braces, and the rules for inserting braces are fully deterministic in C.
if (true)
foo();
else
bar();
This looks prettier to my eyes than if(true){
foo();
} else {
bar();
}
However, I might not be the last person to touch the code. My coworker might come later and add: if (true)
foo();
else
bar();
baz();
And hence the indent problem.Why should the inability to write basic C preclude me from being allowed to pontificate about brace style like the greats?
And besides, code is for humans to read and only incidentally for computers to execute. Might as well optimize for prettiness.
if (true)
foo();
else
bar();
baz();
But I have to admit if you ever actually do that, you have failed to fundamentally understand how your code is executed. No braces means one statement. Period.In these cases languages which are auto formatted (C#, go-fmt, etc) likely have an advantage where these problems stick out more obviously.
I would say I have a pretty good understanding of how C & my code in general works, I'm currently trying to create a threadpool with support for co-routines/yielding to other threads in userspace. Nothing super fancy, but not something you can do without understanding how code is actually executed ;)
But I still lost an hours work last week because I hadn't put a brace after an if statement and when I came back to it, I didn't notice the lack of braces and put an extra statement behind it. This is why I usually stick to putting braces around everything.
if (true) foo();
else bar();
Not much room for a confused baz() here, or so I hope.No holy wars about style please! I was just offering an alternative that works well for me.
(true) ? foo() : bar();
Any style with braces looks like a cluttered mess compared to
if (foo)
throw ...
if (bar)
throw ...
if (baz)
throw ...
But I strictly limit that to the beginning of a function, and only `if (x) [throw|return] y;`you monster.
So much wasted vertical space. We use that at work and I am not at all a fan.
Try it once. You'll never go back.
I see from your comment that you might hire a team of developers to go through a critical path of important C/C++ packages checking for style violations. Any code found missing braces will have a patch submitted upstream to correct the errant style. Any package that declines the style changes would be removed from Debian. Any developer you hire that misses style violations will be removed from the team.
Another approach might be: Turn on the warning mentioned in the article and manually review any cases that come up. That still might generate a lot of cases, but it at least seems possible.
Is there another actionable interpretation of your comment that I'm missing?
This type of bug is largely introduced from someone who is going in and changing a section of code. My philosophy is that if you are editing a section of code, you should update the braces in that section along with your current patch. Over time you see a larger and larger drop in these kinds of bugs as people always, on my team due to the habit of fixing braces and a matching drop from outside contributes due to a larger and larger portion of the code matching the style guide.
Notice that I said "avoided", not "fixed". That is because I am rather focused on making code that is maintainable.
I posted this original comment to have a dialogue about something I include in my programming practices. I wanted to see how other people viewed this as a development technique.
C doesn't do this, but I like this feature from Perl for single-statement checks:
return if is_red($traffic_light);
return unless is_green($traffic_light);
In C, I usually put the early returns on their own line without curly braces, but place them all together at the start of the function to make it easier to spot inconsistencies. if ( $foo ) {
bar();
baa();
}
quux() if $foo;
And these are not: if ( $foo ) bar();
{ bar(); baz(); } if $foo;What I often see is people just ignore the output from tooling and commit anyway.
No method of prevention is perfect, but everything you do can help to increase stability in projects.
Yes, both are policy, but they are different kinds.
Of course, banishing single line blocks at the compiler would be infallible, but it's also not viable.
(Personally my preference would be a linter that automatically runs on checkin, and refuses commits that do not conform to the style guide)
one person's clutter is another person's markers. I wouldn't agree they reduce readability
What screen size? What resolution? What ide/editor window size? What font face & size? With or without soft line wrapping?
Honestly arguments about how "pretty" code is are fucking ridiculous. Despite what someone said, the purpose of source code is not to be "read" with "running" as a secondary task. That literally only applies to code written purely for educational purposes.
The purpose of code is to achieve the goals of the software as efficiently as possible. Efficiency is not just about speed - a fast but unreliable program is not efficient.
Does it matter? There will be a point at which an extra line makes the difference. And particularly with modern screen shapes, vertical space is at much more of a premium than horizontal space.
> Honestly arguments about how "pretty" code is are fucking ridiculous. Despite what someone said, the purpose of source code is not to be "read" with "running" as a secondary task. That literally only applies to code written purely for educational purposes. > The purpose of code is to achieve the goals of the software as efficiently as possible. Efficiency is not just about speed - a fast but unreliable program is not efficient.
The ability to understand software is vital to real-world usefulness though. Requirements change all the time, and so the ability to make changes to software is vital - and to effect desired changes to code you must first understand it.
What? Modern 10:16 screens give more lines than 3:4. 900x1440 gets like 70 lines with 100 columns at a decent font size, better than around 60 lines with 110 columns with 960x1280.
The exact term used was pretty.
> There will be a point at which an extra line makes the difference
So why not remove all blank lines too. They're less important for reducing issues that single line/braceless flow control blocks can cause.
By and large I do. But I don't think what you say is actually true. Given the choice between:
stepa1
if(something)
stepa2
stepa3
stepb1
stepb2
stepb3
and stepa1
if(something) {
stepa2
}
stepa3
stepb1
stepb2
stepb3
(same number of lines), I think the former is often more readable - stepa2 is visually separated in either case.The only code where readability isn't as important as functionality is finished code, and we all know that code is never finished.
I'm talking about people who skip braces on single statements, skip semicolons in JavaScript, etc because it "looks prettier" without them
if(condition_a && condition_b){
do_thing_a();
} else if(condition_b){
do_thing_b();
} else {
do_something_else();
}
vs if(condition_a){
if(condition_b){
do_thing_a();
} else {
do_thing_b();
}
} else {
do_something_else();
}
Furthermore, I prefer functional languages where if-then-else is an expression with a mandatory else (or doing control flow via pattern matching with enforced exhaustiveness like you can get with GHC). I don't like surprises. if( condition_a )
{
if( condition_b )
{
do_thing_a();
}
else
{
do_thing_b();
}
}
else
{
do_something_else();
}
Editor space is free, why not use it.The more lines that are visible on your screen, the less you have to keep in your working memory. Human memory is fragile, so you really don't want to rely on it.
I can only fit 51 lines vertically (damn widescreen laptop) so that one snippet fills a good 1/3 of my screen.
Personally I'd write that as
if (condition_a) {
if (condition_b) do_thing_a();
else do_thing_b();
}
else {
do_something_else();
}Sure if you only saw a few lines at a time, then this might be a problem. But you can see 51 on a laptop. This is enough for almost all cases. And you can scroll if you need to look up something. If you stumble upon a case of multiple if statement that span several screens then no style is going to help you see it in full. In that rare case you might see a little more, at the expense of all code ever becoming less readable (if we assume Allman is more readable for the sake of this argument of course).
If for some reason your code has more than, let's say >50% cases where something spans multiple screens and needs to be seen as a whole (code which should be refactored, but let's ignore that), then you might have an argument to use your style, in all other cases you're doing premature optimization of your coding style, so to speak.
However I also always comment nested logic chains with their intent in plain language. I have made bugs in going from intent->complex logic to often, and I found that writing comments at each fork greatly reduces these errors as well as the odds of missing an edge case.
e.g., I'd write "if(a) and not b" in plain English at the nested else statement, possibly followed by a "what & why" comment at the do_thing_b statement.
if A and B:
..
elif B:
..
vs if A and B:
..
elif A and not B:
..
I prefer flattening because enumeration makes it obvious what's different between the branches despite the code duplication.For example, three boolean conditions has 2^3 = 8 possible combinations. When nested this complexity is hidden but obviously apparent as a code smell when flattened.
It also echoes Python's ethos that flat is better than nested.
We've been doing this at work for several years ; and we found that this solved nearly every style-related issue (diffs, arguments over which code is 'prettier', artificial merge conflicts). It turns out the style becomes a lot less important issue once you can rely on a tool to apply it for you. And it also solves the misleading indentation issue (probably by preventing it to happen in the first place by causing a merge conflict).
// comments that get formatted correctly but then // the // wrapping // isn't // merged // into // one // line
On the other hand
// this could // be a // tabular comment
So you'd need everyone to use the same tool and you'd still need to go back and fix things periodically. To be fair, it might still be a net win.
Cython is already something sort of like this.
One more case to support -Werror.
What's the difference if compilation stops at the warning? You'll (or your team, or whoever) fix it anyway, and if you won't, you have a people problem, that must be solved at the policy (or HR) level.
Solving people problems at the tooling level is a certain way to get unintended consequences and alienate the good people that weren't part of the problem. Of course, you can get some tooling to support your people, but tools to police them are worth less than zero.
Anyway, unrelated to that, I do like to live warnings on code that is not ready to production. It's an easy (effortless in fact) way to make sure it'll be fixed before deploying.
As compilers change they can end up adding new warnings to the defaults, or for or example -Wall, or -Weverything. And different compilers might throw different warnings on the same code with the same options involved. Using -Werror will force an error, which can end up terminating the build for users, even when the code compiles cleanly on your version of your compiler.
So -Werror is fine if you are shipping binaries, but if you are shipping source code, it's probably a good idea to not use it in the public build system.
To parse a C file correctly a tools needs to know the exact set of include path and defines you passed to the compiler. Then it needs to go read the whole tree of header files and preprocess everything. It needs to know where your standard header files are (different from the system header file when cross compiling). It needs to know about your compiler's built-in defines. It may also choke on any C extensions that your code (or any header file) is using.
In this particular case it may be enough to parse the file with some regexes but I wouldn't trust it; people do some crazy things with C macros.
if (foo);
{
bar();
}