when GOTO makes sense.

  • Thread starter Debashish Chakravarty
  • Start date
D

Debashish Chakravarty

K&R pg.66 describes two situations when using goto makes sense. Has
anyone here come across situations where using goto provided the most
elegant solution.
 
M

Mark F. Haigh

Debashish said:
K&R pg.66 describes two situations when using goto makes sense. Has
anyone here come across situations where using goto provided the most
elegant solution.

Yes. Sometimes when...

1. There is code with a lot of resource allocation and/or semaphores
being held and...

2. There are many potential points of failure and...

3. The resources need to be freed in a very precise order on failure...

.... goto usage can be the most elegant solution.


Because of #1 it's very important to prevent resource leaks and/or
deadlocks. #2 makes it problematic and error prone (for maintenence
programmers) to do:

if(error_condition) {
free(a);
free(b);
unlock(c);
unlock(d);
free(e);
return -1;
}

at every point where a failure may occur (there may be 10 of them, and
each may be subtly different). It may be better to have something like:
(again, only a contrived example, assuming a, b, c, e are initialize
to NULL, etc)


if(error_condition)
goto err;

/*...*/

err:
/* Order important !! */
if(a) free(a);
if(b) free(b);
if(c) free(c);
unlock(c); /* must unlock c before d */
unlock(d); /* must unlock d before free(e) */
if(e) free(e);
return -1;
}


Mark F. Haigh
(e-mail address removed)
 
P

Paul Hsieh

Debashish Chakravarty said:
K&R pg.66 describes two situations when using goto makes sense. Has
anyone here come across situations where using goto provided the most
elegant solution.

The classic case is breaking out of a nested loop from the inside.
While people who hate goto for some reason despise such things and
prefer having extra if-checks to account such a condition, its slower,
and just makes your code more complicated.

But another interesting case is with state machines. While the
classic implementation of a state matchine is to simply put a switch
statement inside of a while (1) { ... } loop, this has a drawback of
requiring the switch statement to be execute on every state change (on
modern processors this is much slower than direct gotos.) But as an
alternative, one could simply have one switch statement and rather
than breaks at the end of each case block, simply have gotos to the
blocks that represent the state transition. You have to have labels
that are redundant with the case ...: statements, however this
solution is much faster, and doesn't require that you maintain a state
variable (since the state itself would be redundant with the case
clause that was being executed at any one time) for all states (just
entry and exit of the state machine.)

Finally sometimes its best, for performance reasons to start a loop in
the middle of the body of the loop rather than a start. So you have a
goto just before your do {...} while or for() loop to enter in the
middle of it.

These are probably the most interesting marginal cases where using
goto is the best solution in the C language. The problem is that
there are many other uses of goto which are really really bad. So
much so that people have railed against its usage, and will actually
choose not to do so in any situation. They will lose performance
and/or make their code complicated in all of the above cases. So this
*should* make a good case for improving improving the language so that
these operations can be done *without* goto (for example <break n;> to
jump out of n nested blocks, and <continue case ___;> to jump to a
case within the nearest enclosing switch scope.) However, I don't
know if the C standards committee is open to suggestions such as
these.
 
M

Michael B.

But another interesting case is with state machines. While the
classic implementation of a state matchine is to simply put a switch
statement inside of a while (1) { ... } loop, this has a drawback of
requiring the switch statement to be execute on every state change (on
modern processors this is much slower than direct gotos.) But as an
alternative, one could simply have one switch statement and rather
than breaks at the end of each case block, simply have gotos to the
blocks that represent the state transition. You have to have labels
that are redundant with the case ...: statements, however this
solution is much faster, and doesn't require that you maintain a state
variable (since the state itself would be redundant with the case
clause that was being executed at any one time) for all states (just
entry and exit of the state machine.)

Hmm, I've always implemented state machines with function pointers, like
so:
.... in the header:
typedef void * (*state) (int);
.... in main.c:
while (ch = getnewinput()) {
current = (state)(*current)(ch);
}
}
.... example state: ( this is a very simple DFA )
void *
green (int input) {
switch (input) {
case 'A':
printf("LIGHTS OFF\n");
return &other;
break;
case 'B':
printf("LIGHTS OFF\n");
return &initial;
break;
}
}

Of course, casting function pointers to void and back can lead to
undefined behavior on certain architectures (though I've verified that
this code works on x86 and PPC). But doesn't this seem like a better
solution (and definitely more functional, seeing as it basically emulates
the workings of a functional language), than using gotos?
 
C

Christian Bau

"Michael B. said:
Of course, casting function pointers to void and back can lead to
undefined behavior on certain architectures (though I've verified that
this code works on x86 and PPC). But doesn't this seem like a better
solution (and definitely more functional, seeing as it basically emulates
the workings of a functional language), than using gotos?

It leads to undefined behavior, that's it. So that makes it in the eyes
of most people a very bad solution, definitely worse than using goto.

It also mixes up "states" and "implementation of code to handle the
action in a given state". Each of your functions doesn't return a state,
it returns a pointer to the code to be executed in that state.

And it adds significantly to the number of lines of code to be written.
In most state machines, the code for each state is quite trivial, so
adding a few lines for every state adds significantly to the lines of
code.
 
J

James Dow Allen

Mark F. Haigh said:
Yes. Sometimes when...
2. There are many potential points of failure and...
3. The resources need to be freed in a very precise order on failure...

These gotos may sometimes be avoided by replacing
if (! step7())
goto backout7;
if (! step8())
goto backout8;
...
with
if (step7()) {
if (step8()) {
...
}
}
but the cure may be worse than the disease
if there are many levels of nesting.

Another frequent case where goto is better than the alternative is
for (Search; unfinished;) {
for (Subsearch; unfinished;) {
if (WE_WON) {
celebrate();
goto persevere;
}
}
}
lament();
persevere: ;

(Even when loops are not nested, a ``break'' is not quite
enough to avoid this ``goto.'')

I use very few gotos in *final* code, but when cranking out
fresh code, I sometimes write ``goto start_over'' and then decide
later, when I see the full logic, whether to rewrite the loop as
``do'', ``while'' or ``for.'' (In other words, the goto-less
version will be more *readable*, but the *goto* is more *writable*.)

Finally, let me mention my Favorite Goto (tm):
http://freepages.genealogy.rootsweb.com/~jamesdow/Tech/dbldum.htm

If one needs to make major changes to the Favorite Goto program,
getting rid of the goto might be the first step, but as is,
it possesses, IMO, a beautiful elegance no non-GOTO version
will have. Can you construct a goto-less equivalent that is
in the same ballpark for speed and readability?

(I have posted the Favorite Goto before on Usenet, receiving
some suggestions which made no sense. Please code, compile and
test your version before sending it to me.)

James
 
D

Dan Pop

In said:
K&R pg.66 describes two situations when using goto makes sense. Has
anyone here come across situations where using goto provided the most
elegant solution.

'Elegant' is in the eye of the beholder. I prefer to speak in terms of
readable code. The code readability decreases exponentially when the
number of nested control structures increases linearly. If a goto can
avoid an additional nesting level, than that goto is probably a "good"
goto. Of course, this is not the only technique for minimising the number
of nested control structures in a function, but one shouldn't
automatically discard it on religious grounds.

Dan
 
E

Ed Morton

James Dow Allen wrote:

Finally, let me mention my Favorite Goto (tm):
http://freepages.genealogy.rootsweb.com/~jamesdow/Tech/dbldum.htm

If one needs to make major changes to the Favorite Goto program,
getting rid of the goto might be the first step, but as is,
it possesses, IMO, a beautiful elegance no non-GOTO version
will have. Can you construct a goto-less equivalent that is
in the same ballpark for speed and readability?

This MAY (or may not) be the most efficient piece of code on the planet,
but this has to be some of the worst code I've ever seen for
readability. In particular, jumping back and forth between the bodies of
2 separate pairs of nested "for" loops using "goto"s is horrible, and
then having compound conditions like this in a loop:

for (tnum = won = success = 0;
success ? ++won < need : won + ohsize >= need + tnum;
tnum++, P++, success = IS_CONT(pwin)) {

is just ridiculuous too.

By the way, the program hangs on the second sample input provided on the
web site. I would debug it, but I don't want to take the time to
understand it ;-). Ditto for providing a non-goto alternative.

I'm not 100% against gotos, I've just never seen code that uses gotos
that I personally find more readable than the equivalent without gotos.
I recently wrote a short (200 lines or so) program using gotos just to
see if I could learn to appreciate the benefits wrt error-handling, but
once the error-handling became even moderatly complex (i.e. realisitc!)
the initial simplicity afforded by the gotos fell away. I looked at your
code hoping to learn a good way to use goto for readability.

Ed.
 
T

The Real OS/2 Guy

I use very few gotos in *final* code, but when cranking out
fresh code, I sometimes write ``goto start_over'' and then decide
later, when I see the full logic, whether to rewrite the loop as
``do'', ``while'' or ``for.'' (In other words, the goto-less
version will be more *readable*, but the *goto* is more *writable*.)

Looks more obscure than needed. A better structure is required.

Use functions to encapsulate details from the flow.
Using the the same functions would remove any need for goto as side
effect.
You've written some loops looking exactly identical. Buildng fuctions
would remove the details from the flow and lets them reused.

Programming starts with design, not hacking.
A well designed program has not a single function but many of them,
each not longer than about 50 lines, doing one specific task. Not
more, not less.

A task gets splitted in What is to do and how is it done. This makes
debugging more easy, helps to understund the flow and is at least more
readable as such a hack as you'd produced.
If one needs to make major changes to the Favorite Goto program,
getting rid of the goto might be the first step, but as is,
it possesses, IMO, a beautiful elegance no non-GOTO version
will have. Can you construct a goto-less equivalent that is
in the same ballpark for speed and readability?

A side effect would be that there is no goto in the whole program.
That is because the need of goto gets superflous by design.
 
A

Alan Balmer

K&R pg.66 describes two situations when using goto makes sense. Has
anyone here come across situations where using goto provided the most
elegant solution.

Yes.
 
P

Paul Hsieh

Michael B. said:
Hmm, I've always implemented state machines with function pointers, like
so:

Function pointers, computed gotos (for gcc fans) and switch statements
are all roughly the same speed -- i.e., they are all slow pipeline
serializing bottlenecks in modern microprocessors (since the predictor
is likely to be wrong.) And once again, with your array of function
pointers strategy requires that you maintain an index as you do state
transitions. Worse yet, you have to write a bunch of little
function-lets just to handle single state handling and state
transitions.

Your solution is fine from a functionality point of view, but still
cannot hold a candle to a sequence of hand coded gotos in terms of
performance, and even compactness (since you avoid state index
maintence.) The only disadvantage of my method are the hugely
redundant case and goto labels. (You can of course clean this up a
bit with macros.)
[...] Of course, casting function pointers to void and back can lead to
undefined behavior on certain architectures (though I've verified that
this code works on x86 and PPC).

Of course nobody is going to bother you about doing this, even the
group jumped on me when I magicallly coerced a void * into a FILE *
which apparently fails on like 3 obscure machines in the whole world
that noone has ever heard of (and upon which none can read USENET
news.)
[...] But doesn't this seem like a better
solution (and definitely more functional, seeing as it basically emulates
the workings of a functional language), than using gotos?

No ... not unless you were trying to construct state machines on the
fly. And that doesn't come up unless you are trying to do something
like regular expression parsing -- but in those cases I would think
that special casing would tend to still win over such an overly
general method such as you describe.
 
D

Damn Skippy

I'm not 100% against gotos, I've just never seen code that uses gotos
that I personally find more readable than the equivalent without gotos.
I recently wrote a short (200 lines or so) program using gotos just to
see if I could learn to appreciate the benefits wrt error-handling, but
once the error-handling became even moderatly complex (i.e. realisitc!)
the initial simplicity afforded by the gotos fell away. I looked at your
code hoping to learn a good way to use goto for readability.

Ed.

instead of using 'break' in the pseudocode below, i would prefer using
'goto'. Suggestions appreciated. This avoids testing a variable we
just set (in the event something failed). "var is false. Is var
true?" If there is only one label, it seems very readable to me. It
clearly outlines which parts of the code will not be executed if
something fails. The if() test around process(); suggests at first
glance that it may or may not be executed on an error-free pass,
depending on some other condition. Using goto and keeping process()
from being buried keeps this much more clear. It depends on the
situation of course.

To me,

if ( a) { ... }

reads the same as

if (!a) goto skip_this; { ... } skip_this:

especially, and i will be flamed for this, if you consider the opcodes
which will be generated, on the actual real tangible machine(s) on
which it will run. When you think of it this way, it doesn't matter
whether you use goto or not, cos the CPU will (i.e. JUMP type of
instruction).

So you must decide what is readable, and what other people will think
is readable. Bottom line - if people keep asking you why you have all
those 'goto' statements, it's time to cut back.

Remember, C code will be:
1) compiled and executed or
2) interpreted or
3) in a textbook, hopefully with rules and exceptions


============ inefficient, inelegant, recommended ===========

for ( .. ) {
if ( a != b ) {
good=false;
complain();
break;
}
}

if (good) {
process();
}

cleanup_regardless:
cleanup();

============ better? saves one test and one stack variable ===========

for ( .. ) {
if ( a != b ) {
complain();
goto cleanup_regardless;
}
}

process();

cleanup_regardless:
cleanup();

============ ===========

of course, i hope i never see gotos in something like:

if ( (fp = fopen(szFilename)) != NULL ) {
if ( fread (&buf, 1, len, fp) == len ) {
if ( !strncmp ( buf, 4, "JFIF") ) {
process_jfif();
}
}
}
 
E

Ed Morton

Damn Skippy wrote:
instead of using 'break' in the pseudocode below, i would prefer using
'goto'.

A break (or a continue) is just a goto in disguise. In some ways they're
worse - at least a goto has a label associated with it which you can
give a meaningful name. I never use either, except, of course, break in
a switch statment.

Suggestions appreciated.

Since you asked...

So you must decide what is readable

That's what I'm after. In most code, I'll cheerfully accept a very small
performance impact in exchange for even a very small gain in readability.
============ inefficient, inelegant, recommended ===========

I'd never recommend this....
for ( .. ) {
if ( a != b ) {
good=false;
complain();
break;
}
}

if (good) {
process();
}

cleanup_regardless:
cleanup();

============ better? saves one test and one stack variable ===========

Yes, if modified as described further down in my response.
for ( .. ) {
if ( a != b ) {
complain();
goto cleanup_regardless;
}
}

process();

cleanup_regardless:
cleanup();

============ ===========

What both of the above are missing is an explicit statement of what gets
you out of the "for" loop, so the next person to work on this code has
no help in understanding what this VERY IMPORTANT condition is that
suddenly changes flow control. Assuming that "a != b" means that the
processor is in overload, I'd write it as something like:

olvdState = STABLE;
for ( ..; .. && ovldState == STABLE; .. ) {
if ( a != b ) {
ovldState = OVERLOADED;
complain();
}
}

if (ovldState = STABLE) {
process();
}

cleanup();

If you had written your goto-version like this:

for ( ..; ..; .. ) {
if ( a != b ) {
complain();
goto overloaded;
}
}

process();

overloaded:
cleanup();

then it's pretty close to the version I wrote without gotos in that it
explicitly tells the reader WHY you're leaving the loop (as opposed to
just what you're going to do after you leave the loop, which is already
clear from the "cleanup()" function name) and yes, I think it's better
than your original version that uses a break.

The main reason I prefer the version that uses a variable is that it
forces the original code author to explicitly state the VERY IMPORTANT
loop exit conditions so the next reader isn't fooled into thinking they
know exactly why the loop terminates until they're in the middle of the
loop and suddenly come across an unexpected, probably unnamed, loop exit.

Sure it's a little more work for the original author to come up with
good names for the variable and it's values, but that's better than the
next guy having to potentially take much longer to have to figure out
that abstraction, possibly under customer pressure to fix or enhance
that and/or other code.

People using gotos typically (but, I assume, not always) create their
labels based on WHAT they're doing at that labelled point rather than
WHY they're doing it. Not being a goto officianado, I can't decide if
that's the right thing to do with a goto or not - you'd end up with more
labels if you use the "WHY" (e.g. since there could be many reasons for
going to a "cleanup" routine) but IMHO you'd have more readable code.
of course, i hope i never see gotos in something like:

if ( (fp = fopen(szFilename)) != NULL ) {
if ( fread (&buf, 1, len, fp) == len ) {
if ( !strncmp ( buf, 4, "JFIF") ) {
process_jfif();
}
}
}

Then don't look at the URL posted elsethread (shudder...) ;-).

Ed.
 
J

James Dow Allen

Ed Morton said:
This MAY (or may not) be the most efficient piece of code on the planet,
but this has to be some of the worst code I've ever seen for
readability. In particular, jumping back and forth between the bodies of
2 separate pairs of nested "for" loops using "goto"s is horrible,

Is it really as horrible as it seems? Putting verb participles at the
ends of sentences seems wrong to Englishmen but normal to a German.

I'll certainly admit the structure is bizarrely unusual, but personally I
find it bizarrely elegant! It works only because the two loops manipulate
the same variables in exactly the same way, but once you understand this
fact, it makes sense.

But let me emphasize one key point: The problem this program solves
is non-trivial. *Any* program to solve the problem will take time
to understand. The question isn't whether my program takes a while
to understand, but how that time compares with an efficient alternative
to *solve the same problem.*

It did not take me long to write this program, and many people have
downloaded it for its intended application. Does no one care to invest
a little time to prove there is an efficient readable alternative?
then having compound conditions like this in a loop:

for (tnum = won = success = 0;
success ? ++won < need : won + ohsize >= need + tnum;

I could have added comments, but the C isn't too opaque.
Reading left to right,
did contractors SUCCESS-fully win the trick? If so, increment the
number of tricks they've WON and compare it with the number they NEED
for their contract....
tnum++, P++, success = IS_CONT(pwin)) {

is just ridiculuous too.

Perhaps I should enter it in the Obfuscated C contest. :)
I often provide newlines to break up such long expressions; would
that address your complaint? Is an expression terminated with
comma *really* harder to understand than one terminated with semicolon?
By the way, the program hangs on the second sample input provided on the
web site.

How long did you let it run? Experts spend hours on that puzzle
without getting it, I hope you gave your Pentium a few minutes.
This is the first failure report I've had; I'd appreciate a core dump.

Someone said:
Looks more obscure than needed. A better structure is required.

No question that this 200-line program is much harder to understand
than a typical 500-line program, but that's not the proper question.

The question is, how does its difficulty compare with an alternative
program *that solves the same problem.* Since my program provides
all the necessary algorithm details, I do not believe rewriting the
program is a significant challenge to a good programmer.

When someone demonstrates a working program almost as efficient and
more readable than mine, I'll "eat my hat." Until then, your words
are just idle pedantry.

James
 
E

Ed Morton

James Dow Allen wrote:
It did not take me long to write this program, and many people have
downloaded it for its intended application. Does no one care to invest
a little time to prove there is an efficient readable alternative?

It's not a little time. I'm sure my total lack of knowledge about bridge
doesnt help, but that's kinda my point.
I could have added comments, but the C isn't too opaque.
Reading left to right,
did contractors SUCCESS-fully win the trick? If so, increment the
number of tricks they've WON and compare it with the number they NEED
for their contract....



Perhaps I should enter it in the Obfuscated C contest. :)
I often provide newlines to break up such long expressions; would
that address your complaint?

No, some well-named intermediate variables, functions and/or macros
would address my complaint.

Is an expression terminated with
comma *really* harder to understand than one terminated with semicolon?

No. It was the loop termination condition that was the hardest to
understand. I don't mean I can't read the C, I just don't know what the
abstraction is for the C (i.e. what it "means").
How long did you let it run? Experts spend hours on that puzzle
without getting it, I hope you gave your Pentium a few minutes.
This is the first failure report I've had; I'd appreciate a core dump.

I was wrong, it doesn't hang. It does just take a few minutes. Since the
first sample terminated within about a second, I expected the second to
do the same. Perhaps a warning about that on the web page and/or
progress reports from the tool would be useful?
No question that this 200-line program is much harder to understand
than a typical 500-line program, but that's not the proper question.

The question is, how does its difficulty compare with an alternative
program *that solves the same problem.* Since my program provides
all the necessary algorithm details, I do not believe rewriting the
program is a significant challenge to a good programmer.

But it does provide a significant challenge because it's missing all the
abstractions for what the code is doing, so someone who doesn't know the
domain can read the code and understand what's happening to the
variables, etc., but not why it's happening and without the relevant
domain expertise couldn't come up with the right abstractions for a good
modified version of the code.
When someone demonstrates a working program almost as efficient and
more readable than mine, I'll "eat my hat."

Well, if you decomposed what you have into a few small functions (with
their own local variables rather than just the slew of them at the start
of "main()" being promoted to global) and provided some good, abstract
comments, then you'd stand an excellent chance of getting a meal ;-).

Until then, your words
are just idle pedantry.

You can dismiss my comments if you like, but I wasn't pedantic about
anything. I didn't say anything like "this code uses goto and therefore
is bad" I said "This code is hard to read, and here's some examples of why".

Ed.
 
S

stelios xanthakis

Debashish Chakravarty said:
K&R pg.66 describes two situations when using goto makes sense. Has
anyone here come across situations where using goto provided the most
elegant solution.

Another very useful case:

if (..complex stuff..) {
do_something ();
if (...other complex stuff) goto eelse;
... do stuff ...
} else eelse: {
... do the else case ...
}

Once you do this, you'll want to do it all the time.
 
E

Ed Morton

stelios said:
Another very useful case:

if (..complex stuff..) {
do_something ();
if (...other complex stuff) goto eelse;
... do stuff ...
} else eelse: {
... do the else case ...
}

This is clearer:

status = INCOMPLETE;
if (..complex stuff..) {
do_something ();
if (! ...other complex stuff) {
... do stuff ...
status = COMPLETE;
}
}
if (status == INCOMPLETE) {
... do the else case ...
}

Of course, In a real program I'd have a variable name and values that
accurately reflect the abstract conditions that are true at both of the
assignment points for "status" above.
Once you do this, you'll want to do it all the time.

Not really.

Ed.
 
S

stelios xanthakis

Ed Morton said:
This is clearer:

status = INCOMPLETE;
if (..complex stuff..) {
do_something ();
if (! ...other complex stuff) {
... do stuff ...
status = COMPLETE;
}
}
if (status == INCOMPLETE) {
... do the else case ...
}

Right. Only "problem" is that now we have two statements (actually 3 statements
and a variable). So everything has to be enclosed in { } if the above
is, say, the body of a for(;;).
This is a personal preference of course with little practical use.

Stelios
 
E

Ed Morton

stelios said:
Right. Only "problem" is that now we have two statements (actually 3 statements
and a variable). So everything has to be enclosed in { } if the above
is, say, the body of a for(;;).

Which it should be anyway to avoid someone in future coming along and
adding a statement which they think is related to the "for" but actually
falls outside it or pushes the existing code outside of it. That's a
very common bug when modifying code that doesn't use braces on every
loop and "if". There's been so many times I've seen something like this:

if (x)
y();

get changed to:

if (x)
y();
z();

Some people write:

if (x) y();

to make the distincion clearer for single-statement cases, but it seems
simpler to me to just always uses the braces (and there are implications
for some source code control systems in a multi-user environment) and
that doesn't apply in this particular case anyway.
This is a personal preference of course with little practical use.

No, it's about increasing the clarity of your code for speed of
understanding by the next person to enhance it and reducing the risk of
someone in future introducing a bug when they modify it (i.e.
"future-proofing") and it's a 100% practical technique.

Ed.
 
A

Alan Balmer

Right. Only "problem" is that now we have two statements (actually 3 statements
and a variable). So everything has to be enclosed in { } if the above
is, say, the body of a for(;;).

And, imo, this is a good thing. Braces are free, and useful.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top