Or You Could Simplify The Error-Recovery Paths
Or You Could Simplify The Error-Recovery Paths
Posted Dec 25, 2014 21:34 UTC (Thu) by cesarb (subscriber, #6266)In reply to: Or You Could Simplify The Error-Recovery Paths by ldo
Parent article: The "too small to fail" memory-allocation rule
> if (failure)
> break;
> } while (false);
Wouldn't it be simpler and more readable to use a goto instead of a pseudo-loop?
(The Linux kernel does use the "set pointers to NULL and on error free them if they are not NULL" paradigm, of course using a "goto error" instead of a pseudo-loop, but the error-recovery paths being discussed are not merely "freeing locally allocated memory"; they can also have things like "unlocking a mutex" or "updating the filesystem block allocation structures to mark as free the blocks which had been reserved for this task".)
Posted Dec 25, 2014 22:08 UTC (Thu)
by ldo (guest, #40946)
[Link] (65 responses)
Posted Dec 25, 2014 22:23 UTC (Thu)
by mchapman (subscriber, #66589)
[Link] (27 responses)
There would be precisely as many "goto" statements as you have "break" statements. It's much of a muchness, in my opinion.
Posted Dec 25, 2014 23:07 UTC (Thu)
by ldo (guest, #40946)
[Link] (26 responses)
And you would need a different label for each. And the extra visual burden of ensuring that each goto is paired with the right label. Which is unnecessary with a break, since that can only ever terminate one enclosing construct.
And the further burden of getting things wrong when you try refactoring the code.
Like I said: NO GOTOs!
Posted Dec 26, 2014 0:01 UTC (Fri)
by Cyberax (✭ supporter ✭, #52523)
[Link] (5 responses)
So, YES to gotos for error handling!
Posted Dec 26, 2014 0:37 UTC (Fri)
by ldo (guest, #40946)
[Link]
Posted Dec 26, 2014 7:38 UTC (Fri)
by epa (subscriber, #39769)
[Link] (3 responses)
Posted Dec 26, 2014 7:43 UTC (Fri)
by Cyberax (✭ supporter ✭, #52523)
[Link]
Posted Jan 16, 2015 5:10 UTC (Fri)
by CameronNemo (guest, #94700)
[Link]
Posted Jan 16, 2015 18:23 UTC (Fri)
by zlynx (guest, #2285)
[Link]
That almost always made the code cleaner, and let me exit the block (which was now a function) with a simple "return."
Posted Dec 26, 2014 7:34 UTC (Fri)
by WolfWings (subscriber, #56790)
[Link] (19 responses)
Um... since when? You can use a single label for any number of goto's, so each label is specifically describing it's use case (freeAndExit perhaps, or whatever semantic name you want to give it) and there's no more/less lines than your do{}while(false); construct but copy-pasting code inside/outside the strcture can't break it as easily. Just saying you're wrong about the claim of needing a billion labels, goto is many-to-one not one-to-one in that regard.
Posted Dec 26, 2014 21:15 UTC (Fri)
by ldo (guest, #40946)
[Link] (17 responses)
Posted Dec 27, 2014 0:47 UTC (Sat)
by reubenhwk (guest, #75803)
[Link] (7 responses)
The deeply nested allocations example *should* be solved by factoring out local/static helper functions...In other words: if your code is too complicated to use GOTO's properly for cleanup, then your code is too complicated.
Posted Dec 27, 2014 20:42 UTC (Sat)
by ldo (guest, #40946)
[Link] (6 responses)
Posted Dec 27, 2014 20:56 UTC (Sat)
by Cyberax (✭ supporter ✭, #52523)
[Link] (1 responses)
What else do you want?
Posted Dec 27, 2014 21:33 UTC (Sat)
by ldo (guest, #40946)
[Link]
Posted Jan 1, 2015 0:27 UTC (Thu)
by reubenhwk (guest, #75803)
[Link] (3 responses)
Posted Jan 1, 2015 4:23 UTC (Thu)
by flussence (guest, #85566)
[Link] (1 responses)
Posted Jan 1, 2015 4:40 UTC (Thu)
by reubenhwk (guest, #75803)
[Link]
Posted May 23, 2017 13:10 UTC (Tue)
by mirabilos (subscriber, #84359)
[Link]
Posted Dec 27, 2014 17:44 UTC (Sat)
by itvirta (guest, #49997)
[Link] (8 responses)
> for(...) {
I'm not sure I'd agree that breaking out of a loop just to test the same error condition,
Like someone mentioned, some other programming languages have dedicated constructs
(The thing with absolute rules is, that they absolutely forbid one from thinking. That usually
> Fine. Try reworking the example I gave above, then. That has allocations nested up to three levels
Reducing the nesting level and using a less space-hungry indenting style would be what I'd start with,
Posted Dec 27, 2014 20:46 UTC (Sat)
by ldo (guest, #40946)
[Link] (7 responses)
Posted Dec 27, 2014 20:57 UTC (Sat)
by Cyberax (✭ supporter ✭, #52523)
[Link] (1 responses)
Or here: https://github.com/ldo/dvd_menu_animator/blob/master/spuh... ?
I actually don't see complicated cleanups anywhere in your code. Posted Dec 28, 2014 3:27 UTC (Sun)
by reubenhwk (guest, #75803)
[Link] (4 responses)
Better yet, use a function for each of those nested levels. Worried about spaghetti code? Then factor out the nesting and call smaller functions with less looping and less nested resource management. Use the return value to propagate error conditions onward.
Posted Dec 28, 2014 6:34 UTC (Sun)
by ldo (guest, #40946)
[Link] (3 responses)
Posted Dec 29, 2014 2:58 UTC (Mon)
by reubenhwk (guest, #75803)
[Link]
Posted Dec 29, 2014 3:19 UTC (Mon)
by bronson (subscriber, #4806)
[Link] (1 responses)
Posted Dec 29, 2014 17:30 UTC (Mon)
by nix (subscriber, #2304)
[Link]
The argument from popularity is not a good one, but if something is not popular it is not terribly wise to imply that in fact it is.
Posted May 23, 2017 13:09 UTC (Tue)
by mirabilos (subscriber, #84359)
[Link]
Avoiding GOTO at all cost is overreacting. The “rule” is probably good to teach to novice programmers, but experts are beyond it. Error handling is *the* prime example in favour of GOTO, although I agree there may be others.
Posted Dec 25, 2014 22:36 UTC (Thu)
by vonbrand (guest, #4458)
[Link] (7 responses)
Please, no knee-jerk "no goto ever" reactions, go read Knuth's "Structured programming with goto statements", ACM Computing Surveys 6(4), dec 1974, pp 261-301. Check how goto is used in the kernel, write up gotoless code doing the same. You'll find that the gotoless code is easier to understand and often significantly more efficient.
Posted Dec 25, 2014 23:08 UTC (Thu)
by ldo (guest, #40946)
[Link] (6 responses)
Posted Dec 30, 2014 2:10 UTC (Tue)
by filipjoelsson (guest, #2622)
[Link] (2 responses)
I thought it would be a fun exercise to rewrite your code with the exact same structure that you used. But instead of the You do realize that your construct will optimize to exactly the same code, when compiled, right? So, that all you do is add empty words and pointless levels of indent, that may or may not help compilers and other programmers understand what you mean? So I took a dive into your code, stripped out all the comments and newlines, ran it through indent, and tried again. And I must say that I believe that Why the f*ck do you for instance do things like this? Breaking expectations like that do not anything of value, and makes sure nobody else will want to bother helping out on your project. You also have nuggets like: Which I would like to replace with: I also wonder what the point is of putting semicolons after comments. In conclusion: I was in your camp until this discussion started - I really never ever use goto. You, however, have convinced me that there are reasonable exceptions to that rule.
Posted Dec 30, 2014 20:42 UTC (Tue)
by ldo (guest, #40946)
[Link] (1 responses)
I have this convention: if one of the exits from a loop is via an explicit break, then all exits from the loop shall be via explicit breaks. That way you don’t have to look for more than one kind of termination condition, just look for the breaks. Only if there are none will there be something like a for-loop termination condition. Never mix the two.
Posted Dec 30, 2014 21:12 UTC (Tue)
by filipjoelsson (guest, #2622)
[Link]
Is there any construct that you use as intended?
Posted Dec 30, 2014 3:00 UTC (Tue)
by flussence (guest, #85566)
[Link] (2 responses)
But the compiler can see your royal highness is stark naked. And so can we.
Posted Dec 30, 2014 9:47 UTC (Tue)
by anselm (subscriber, #2796)
[Link] (1 responses)
GOTO used to be a real problem when it was all that people had for control flow (think 1960s-era FORTRAN).
Right now with the proper discipline I don't have a huge problem with “goto” in C. As far as I'm concerned it is way less of a problem for program structure than the style that ldo is advocating. ldo's style also seems to presuppose that “cleanup” consists exclusively of undoing earlier things in reverse order; in general one might have to do things in an error branch that one might not have to do during normal flow, and sorting these cases out just serves to make the code even more convoluted on top of all the extraneous “do { … } while (0)” and “if (…) break” constructs.
I teach programming as my day job (some of the time, anyway), and I can guarantee that anybody who came to me with code like ldo's would get a very stern talking-to.
Posted Dec 30, 2014 20:53 UTC (Tue)
by ldo (guest, #40946)
[Link]
Posted Dec 25, 2014 22:41 UTC (Thu)
by viro (subscriber, #7872)
[Link] (28 responses)
It's both unidiomatic and brittle. Avoiding goto is generally a good idea, but this isn't a good way to do it.
Posted Dec 25, 2014 23:10 UTC (Thu)
by ldo (guest, #40946)
[Link] (27 responses)
That code already has plenty of loops in it, and you can see for yourself how to keep the interactions simple in that situation.
This isn’t a question of “religion”, it’s one of “bitter experience”. Learn from it!
Posted Dec 26, 2014 0:03 UTC (Fri)
by Cyberax (✭ supporter ✭, #52523)
[Link] (26 responses)
Experience shows that idiomatic "goto error_exit" is far better than the alternatives with fake loops and flags.
Posted Dec 26, 2014 0:36 UTC (Fri)
by ldo (guest, #40946)
[Link] (25 responses)
Posted Dec 26, 2014 1:32 UTC (Fri)
by Cyberax (✭ supporter ✭, #52523)
[Link] (24 responses)
I won't bother to rewrite all of the code, so only for ParseColorTuple: https://gist.github.com/Cyberax/4546471dcb026c141369 (sorry for possible issues with indentation).
It's now 10 lines less and much more concise (WTF are you incrementing the for-loop variable manually and then manually check conditions?).
I've also fixed a bug in line 246 (failure to check error immediately).
Posted Dec 26, 2014 2:12 UTC (Fri)
by ldo (guest, #40946)
[Link] (23 responses)
At least you made the effort, even if you “fixed” a non-bug and succeeded in introducing a real bug.
This was a simple, almost trivial case. Now try a more complex one. Elsewhere you will see do-once constructs nested up to 3 deep. And then there’s loops. Try and see how well your goto constructs scale up to those complexities.
Posted Dec 26, 2014 4:59 UTC (Fri)
by Cyberax (✭ supporter ✭, #52523)
[Link] (22 responses)
> This was a simple, almost trivial case. Now try a more complex one.
> Elsewhere you will see do-once constructs nested up to 3 deep.
And the rest of your code...
It pretty much falls into the 'unmaintainable crap' bin. Everything is just perfect: eclectic code style, exotic constructs (inner functions in... C?), profligate usage of goto disguised as do..nothing loops, functions spanning hundreds of lines, etc.
Posted Dec 26, 2014 5:34 UTC (Fri)
by ldo (guest, #40946)
[Link] (20 responses)
Come back when you’ve learned a bit more about programming.
Posted Dec 26, 2014 5:40 UTC (Fri)
by Cyberax (✭ supporter ✭, #52523)
[Link] (13 responses)
Or perhaps an audio codec? Nah, can't write those with gotos: https://git.xiph.org/?p=opus.git;a=blob;f=src/opus_encoder.c;...
Posted Dec 26, 2014 21:21 UTC (Fri)
by ldo (guest, #40946)
[Link] (12 responses)
While I’m sure it is quite complex code, it doesn’t seem to do much with dynamic storage: I found just two calls to opus_free in those 2500-odd lines.
While my example is half that size, it has 39 calls to PY_{,X}DECREF.
Come back when you can cope with that kind of complexity.
Posted Dec 26, 2014 21:25 UTC (Fri)
by Cyberax (✭ supporter ✭, #52523)
[Link] (11 responses)
Posted Dec 27, 2014 20:56 UTC (Sat)
by ldo (guest, #40946)
[Link] (10 responses)
Posted Dec 27, 2014 20:58 UTC (Sat)
by Cyberax (✭ supporter ✭, #52523)
[Link] (9 responses)
Posted Dec 27, 2014 21:43 UTC (Sat)
by ldo (guest, #40946)
[Link] (8 responses)
And while we’re at it, your tun.c example fails to recover if it cannot create its sysfs device files.
Posted Dec 27, 2014 22:01 UTC (Sat)
by Cyberax (✭ supporter ✭, #52523)
[Link] (7 responses)
The Linux kernel manages to do that just fine, somehow.
In particular, your block near line 480 will look like this:
And let me reiterate, your code is a mess. For example, you use 'break' to normally exit the outer loop from within the 'if' statement here: https://github.com/ldo/dvd_menu_animator/blob/master/spuh...
Why do you even bother with 'for' loops?
Posted Dec 28, 2014 6:29 UTC (Sun)
by ldo (guest, #40946)
[Link] (6 responses)
At least it works, unlike your code.
Pot calling the kettle black, as it were?
Posted Dec 28, 2014 6:31 UTC (Sun)
by Cyberax (✭ supporter ✭, #52523)
[Link] (5 responses)
Posted Dec 28, 2014 6:36 UTC (Sun)
by ldo (guest, #40946)
[Link] (4 responses)
Posted Dec 28, 2014 6:41 UTC (Sun)
by Cyberax (✭ supporter ✭, #52523)
[Link] (3 responses)
If you can make a reasonable description of what it does - I can guarantee that it's possible to write it far cleaner then what you have.
Posted Dec 28, 2014 21:33 UTC (Sun)
by ldo (guest, #40946)
[Link] (2 responses)
Posted Dec 28, 2014 22:37 UTC (Sun)
by Cyberax (✭ supporter ✭, #52523)
[Link] (1 responses)
Posted Dec 29, 2014 17:26 UTC (Mon)
by ldo (guest, #40946)
[Link]
What’s good for the goose, eh?
Posted Dec 26, 2014 16:14 UTC (Fri)
by mb (subscriber, #50428)
[Link] (5 responses)
This is like the guy complaining about _all_ those damn ghost drivers on the highway.
Huge projects use goto for error handling successfully. See the Linux kernel for instance.
Posted Dec 26, 2014 21:15 UTC (Fri)
by ldo (guest, #40946)
[Link] (4 responses)
Posted Dec 27, 2014 11:32 UTC (Sat)
by mb (subscriber, #50428)
[Link] (3 responses)
Posted Dec 27, 2014 20:40 UTC (Sat)
by ldo (guest, #40946)
[Link] (2 responses)
Posted Dec 27, 2014 23:14 UTC (Sat)
by vonbrand (guest, #4458)
[Link] (1 responses)
The pseudo-loops hide the error processing in a structure that could be anything else, and which BTW I haven't seen anywhere else either. ln comparison, the "label: <error handling code>" pattern is easy to spot.
Posted Dec 28, 2014 6:33 UTC (Sun)
by ldo (guest, #40946)
[Link]
Posted Dec 26, 2014 16:30 UTC (Fri)
by nix (subscriber, #2304)
[Link]
This is not something that you'd call out as an advantage and a sign of great experience unless you didn't actually have much. (But it's been worth reading this thread anyway: it's not every day you see people telling Al Viro that he doesn't have enough experience to judge the relative worth of exception-handling goto versus other techniques!)
No! No GOTOs. They don’t nest easily, and they lead to spaghetti code.
Look at the example code I linked above, and think how it would look with GOTOs all over the place. Avoid GOTOs!
Re: Wouldn't it be simpler and more readable to use a goto instead of a pseudo-loop?
Re: Wouldn't it be simpler and more readable to use a goto instead of a pseudo-loop?
Re: There would be precisely as many "goto" statements
Re: There would be precisely as many "goto" statements
Re: So, YES to gotos for error handling!
Re: There would be precisely as many "goto" statements
Re: There would be precisely as many "goto" statements
Re: There would be precisely as many "goto" statements
Re: There would be precisely as many "goto" statements
Re: There would be precisely as many "goto" statements
if (error1) {
goto label;
}
/* ... more code ... */
if (error2) {
goto label;
}
/* ... more code ... */
label: free(pointer);
Re: you're wrong about the claim of needing a billion labels
Re: you're wrong about the claim of needing a billion labels
Re: deeply nested allocations example *should* be solved by factoring out
Re: deeply nested allocations example *should* be solved by factoring out
Re: What else do you want?
Re: deeply nested allocations example *should* be solved by factoring out
Re: deeply nested allocations example *should* be solved by factoring out
Re: deeply nested allocations example *should* be solved by factoring out
Re: deeply nested allocations example *should* be solved by factoring out
goto bike shed
> example code I linked above, and think how it would look with GOTOs all over the place.
> Avoid GOTOs!
> if (PyErr_Occurred()) break;
> }
> if (PyErr_Occurred()) break;
to then only break out of another loop is any clearer than just jumping out directly.
(like exceptions) that can break out of multiple loops. It's not very different from what goto
was recommended for in this case.
doesn't lead to anything good, so I would advise against it.)
> deep, as well as loops. See how well that works using GOTOs.
if I were to do this exercise for you.
Re: not sure I'd agree that breaking out of a loop just to test the same error condition
Re: not sure I'd agree that breaking out of a loop just to test the same error condition
Re: not sure I'd agree that breaking out of a loop just to test the same error condition
>> propagating the error condition onwards. That is the whole point of the nesting.
Re: Better yet, use a function for each of those nested levels.
Re: Better yet, use a function for each of those nested levels.
Re: Better yet, use a function for each of those nested levels.
Re: Better yet, use a function for each of those nested levels.
Re: There would be precisely as many "goto" statements
Re: Wouldn't it be simpler and more readable to use a goto instead of a pseudo-loop?
Re: Please, no knee-jerk "no goto ever" reactions
Re: Please, no knee-jerk "no goto ever" reactions
do { /* code */ } while (false);
construct - I'd use goto cleanup
. The point of this would have been to prove to you that what you are doing is in fact to use goto, but in a circumspect manner.do { /* code */ } while (false);
is one of your smaller sins!for (i=0;;) {
if (i == nrcolors) break;
/* code */
++i;
}if (nrhistentries <= 4 || /* other condition */)
{
if (nrhistentries > 0) {
histogram[0].index = 0;
if (nrhistentries > 1) {
histogram[1].index = 1;
if (nrhistentries > 2) {
histogram[2].index = 2;
if (nrhistentries > 3) {
histogram[3].index = 3;
}
}
}
}
int max_histentries = (nrhistentries>4)?4:nrhistentries;
for (i=0; i<max_histentries; ++i) {
histogram[i].index = i;
}Re: Why the f*ck do you for instance do things like this?
Re: Why the f*ck do you for instance do things like this?
Re: Please, no knee-jerk "no goto ever" reactions
Re: Please, no knee-jerk "no goto ever" reactions
Re: in general one might have to do things in an error branch that one might not have to do during normal flow
Re: Wouldn't it be simpler and more readable to use a goto instead of a pseudo-loop?
Re: E.g. a loop or switch added,
Re: E.g. a loop or switch added,
Re: Experience shows that idiomatic "goto error_exit" is far better than the alternatives with fake loops and flags.
Re: Experience shows that idiomatic "goto error_exit" is far better than the alternatives with fake loops and flags.
Re: I won't bother to rewrite all of the code, so only for ParseColorTuple:
Re: I won't bother to rewrite all of the code, so only for ParseColorTuple:
Where?
You can do it _automatically_ by rewriting do-nothing loops into 'goto cleanup' and merging cleanup actions if needed.
That's not something to be proud of, you know. It means that you've failed to decompose your logic into smaller fragments.
Re: It pretty much falls into the 'unmaintainable crap' bin
Re: It pretty much falls into the 'unmaintainable crap' bin
Like... I don't know, say an OS kernel?
Re: Or perhaps an audio codec?
Re: Or perhaps an audio codec?
Re: Your wish is my command
Re: Your wish is my command
Re: What do you mean by "with a loop"?
Re: What do you mean by "with a loop"?
No you don't. Just split them up into free-standing functions, instead of 300-line monstrosities.
https://gist.github.com/Cyberax/3a7796231be66d0f64cc
(no, I'm not going to check it in details). It's pretty clear that simply by splitting some logic into a function you can decrease nesting level by 3.
Re: And let me reiterate, your code is a mess.
Re: And let me reiterate, your code is a mess.
Re: I have not claimed that my code works
Re: I have not claimed that my code works
Re: If you can make a reasonable description of what it does
Re: If you can make a reasonable description of what it does
Re: Please, provide a coherent description
Re: It pretty much falls into the 'unmaintainable crap' bin
And your "nested pseudo loops error handler" argument is void, because thouse unmaintainable monsters should be split into sub-routines anyway. And then it can use plain gotos. Which gains the additional opportunity to write structured error paths instead of "one must handle all errors" error paths.
Re: Huge projects use goto for error handling successfully
Re: Huge projects use goto for error handling successfully
Your complex pseudo-loop can't help here at all. It just adds extra complexity. The error paths will still be untested.
Re: The error paths will still be untested.
Re: The error paths will still be untested.
No, the cleanups always happen after the do-once constructs. That’s how you can be sure they will always be executed.
Re: The pseudo-loops hide the error processing
(do/once constructs nested up to three deep)
Re: I won't bother to rewrite all of the code, so only for ParseColorTuple:
That's not something to be proud of, you know. It means that you've failed to decompose your logic into smaller fragments.
Actually it means he's probably trying to do multiple things which need unwinding, but because he can't use three goto labels and rely on fallthrough he has to add a layer of almost-entirely-redundant nesting for each one.