You could work around this via tons of deeply nested blocks, but that makes the code very unreadable. The goto cleanup pattern makes the code intuitive and very readable.
I still have a printout somewhere of an application from the 80s, maybe a dozen pages in small font, every few lines it jumps via goto to somewhere else, depending on state (global variables, of course). It was a serious nightmare to figure out where the code is going. That was the historical context of "goto considered harmful". It sure was! Code like that doesn't exist today, the lessons have been learned. It had nothing to do with clean jump to a cleanup section (which is what a finally{} block is, after all).
Let's say you need to dynamically allocate two buffers in a function and want to make sure they are freed at the end of your call. You can use this macro like so:
int two_bufs(int n, int m) {
int *buf_1 = NULL;
int *buf_2 = NULL;
buf_1 = calloc(n, sizeof(int));
CHECK(buf_1);
buf_2 = calloc(m, sizeof(int));
CHECK(buf_2);
// ... lots of cool things with buf_1 and buf_2
error:
free(buf_1);
free(buf_2); // Safe if null
}If you hit an error condition in deeply nested logic and need to go to common error handling code or go to a recovery point, then goto makes sense.
Often times in these scenarios simplifying the logic is also an option, but that isn't always the case, especially when dealing with external interfaces that have a lot of potential error conditions that cannot be dealt with easily from the immediate caller (I'm thinking primarily of system interfaces, but also potentially library interfaces).
From what I've seen this is the only really good scenario for goto and I think the examples in the article more or less fall into this category of escaping from deeply nested error cases.
1 every 160 lines is a goto, and that's just a naive count so includes all header and preprocessor gunk, whitespace, some generated C code, comments, etc.
If you're talking about more pure lines if C, it would probably be close to 1% of statements are goto.
It might not be everyone's cup of tea as the pinnacle of C programming, but it's a real highly used medium-large project that has coped well with a pretty high rate of change from a very wide spectrum of contributors. So it is a good real world example of open C code you can look at that is maintainable while making a lot of use of goto.
> "I will prefer to hide one or more of the inner loops inside of a function call."
That risks the additional unnecessary overhead of a function call, which can be expensive in a loop, and not all compilers are as good at optimizing as they should be.
And I would too... but sometimes there are too many local variables involved to do that cleanly. And, ultimately, if you are making your code less clean to avoid using a goto then you avoided unwisely.
> Can you give an example of where you've used goto in C or a C-like language?
Well there have been several fairly normal examples cited by others. Let me set out my stall and present a more controversial one.
Suppose I have a switch() block (which is sorta a glorified goto already!) and I have two cases that have the same epilogue. For example:
switch (get_next_thing_to_do()) {
case ACTION_A:
do_action_a();
goto common_post_action_cleanup;
case ACTION_B:
do_action_b();
common_post_action_cleanup:
cleanup_something();
z = nullptr;
x = x->next;
// yadda yadda
break;
case ACTION_C:
do_action_c();
// NOTE: we don't need to do the "cleanup" here
break;
}
Now the first thing you should try is to put all of the shared code in its own function. Again, this isn't always practical if what you need to do effects a lot of local variables. It also means that those "cleanup" steps are now implemented far away from the rest of the logic which might hurt comprehensibility on its own.Another option is to just suck it up, repeat the code twice, and then just trust that the compiler will clean it up. First, that won't be as attractive if you have to do it 10 times instead of twice. Second, you're now violating the DRY principal which to me is sacrosanct. Experience has taught me that if someone updates the cleanup code for ACTION_A they will forget to update ACTION_B at the same time. If you care that the two cases always end with the same epilogue you need to have a single copy of the code.
You can try to get back into compliance with DRY by using something like a C macro, but now you're not winning the war against ugly code.
Finally you could just put the cleanup code after the switch(), either by retesting the enum value (requiring it to be stored in a temporary variable) or by adding some other new cleanup_needed boolean flag. But now we're adding control flow that wasn't there before. I personally believe that doesn't help readability or maintainability. You're now just adding more variables to understand and probably getting worse generated assembly to boot.
While many programmers will look at this and feel vaguely icky about the presence of a "goto" if you really stop and look at the block of code it's actually quite readable...at least as long as the label and the goto are near each other. It is immediately apparent that two "states" are sharing some code and why.
This little example is obviously made up. However I've spent a lot of my career writing things like hand-crafted state machines and situations exactly like this (where two "states" are different but share the same epilogue) comes up all the time. State machine code like this is famously hard to read but often sharing code via goto like this is the least-bad option for expressing non-hierarchical control flow.
But yep, I have no complaints with the code as presented.
When going through the code, though, be aware that some goto's are in #define's to make it easier to work with setjmp().
If you're lazy, it's about 10k loc with about 100-200 uses of goto.
For example, if a file is locked for writing, I want to wait for 5, then 10, then 15 seconds, retrying each time, before giving up.
In such case I do not want the retrials loop to be my main control structure.
Instead, I jump from inside the IO exception catch block to the ReTry label, restarting the whole operation.
This makes the flow fairly clean.
Your suggestion adds indirect memory access plus function call, for every instruction.
I've tried every other way I can think of, and nothing runs faster from my experience.
In some of those cases there are alternatives that could be better than goto, but they cannot be used because they need features that are missing from the programming languages popular now.
One example is state machines. Many programmers when having to implement a state machine use a state variable, e.g. an enumeration and a big "switch" or "case" structure, depending on the language, with a branch for each state.
This kind of implementation follows the method taught now to avoid goto's by adding unnecessary variables that are used to store some state when some condition is detected with the purpose to make a jump later with an extra if or switch instead of doing a jump with a goto immediately when the condition has been detected.
This method of eliminating goto's is pretty stupid, because it not only adds useless variables but it also doubles the number of conditional branches, because each
if (condition) goto label;
is replaced by:
if (condition) variable = value;
...
if (variable == value) {...} or switch (variable) {...}
Besides the accesses to non-cached memory, the conditional branches are the main causes of low performance, so doubling the number of conditional branches can result in much worse performance.
When the second conditional branch is a "switch", that is even worse, because it is typically implemented as an indexed jump, which will be frequently mispredicted.
Instead of using a state variable and a "switch" with branches for states, the right implementation is to have sequences of statements for the states, with the first statement in each sequence labeled with the name of the state.
During each state, when the condition for the transition to another state is detected, a goto is executed, with the name of the next state.
Anyone who believes that this is less clear or more difficult to read than the variant with state variables is delusional, because in the variant with goto's you see immediately the effect of a transition condition, while in the variant with state variables you just see some value being stored and you do not know its purpose until possibly much later when you discover that it is tested for a second conditional branch. Even after you see this use, you are not certain that this is the only place where the variable is used, it might also have other uses and you might need extra time until you are certain about what the program really does.
A more elegant solution for state machines is possible only in languages where tail call optimization is guaranteed.
In such languages each state may be implemented as a separate function and the goto's may be replaced with function invocations in final positions, which are transformed by the compiler into jumps.
While not so many programmers need to write state machines, there are much more frequent uses where goto is superior, e.g. for error management in cases where exceptions are inappropriate, but describing when, why and how would be long.
In general all the problems for which goto was considered harmful are not solved by eliminating goto but by restricting its behavior.
There was a series of research articles by various authors, published between 1970 and 1975, whose conclusion was that the right kind of goto should be allowed to make only forward jumps and that the labels must have a scope restricted to the block in which they are declared. Therefore a goto should be able only to exit from a block but not to enter a block.
An example of a programming language that had this restricted form of goto was the language Mesa, from Xerox.
In C however, it would not be possible to restrict goto only to forward jumps without simultaneously adding guaranteed tail call optimization.
I agree with your post in principle, but in practice I can't think of a single example where I've used a goto that wasn't eventually refactored to something better that didn't have the goto.
Edit: Probably the most common example (and one given early in the article) is deeply nested loops. This is a good example of where I'm very unlikely to use goto. Almost certainly, I will prefer to hide one or more of the inner loops inside of a function call.