GoTo: is it really evil?

Started by
151 comments, last by Troll 18 years, 1 month ago
Quote:Original post by TANSTAAFL
Some stuff I found on the web on this topic:

http://icarus.cs.weber.edu/~dab/cs1410/chap.03/goto_statements.pdf

http://icarus.cs.weber.edu/~dab/cs1410/chap.03/comments.cpp

I have to disagree that that is a good use of goto. Maybe its just my training, but I find state machines to be infinitely more useful if you actually have states, instead of a horrible mess of jumps. If nobody else does so in the mean time, I'll rewrite that particular machine later today.

CM
Advertisement
Quote:Original post by lhead
@Promit: In the usual case, and most of the simplified examples presented here, this is a nice solution. However, your function has no access to member variables of "another function somewhere", which is often desired - you need to pass them as arguments. And dropping the 'break' is possible because you have only one exit point.
All valid points. But I don't seek to provide a general solution to all uses of goto. I tackled one specific case in the most elegant way I could think of, and given a different bit of code I might choose a different approach. It's just that I have yet to see any bit of code where the use of goto was even vaguely compelling, and I prove it by cleanly fixing up the code, while improving the overall readability.

Anyone should feel free to present another example. The local function inside a struct (it's unfortunate that local function definitions never really caught on) does provide a somewhat more general purpose solution. Personally, I don't much buy general case solutions as being more than a guideline. Any given situation generally has quirks which allow you to make more effective refactorings.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Quote:Original post by Conner McCloud
Quote:Original post by TANSTAAFL
Some stuff I found on the web on this topic:

http://icarus.cs.weber.edu/~dab/cs1410/chap.03/goto_statements.pdf

http://icarus.cs.weber.edu/~dab/cs1410/chap.03/comments.cpp

I have to disagree that that is a good use of goto. Maybe its just my training, but I find state machines to be infinitely more useful if you actually have states, instead of a horrible mess of jumps. If nobody else does so in the mean time, I'll rewrite that particular machine later today.

CM


While I don't think that example was a horrible mess, I too would refactor it. That said, that's probably going to be one of the fastest running parsers. While speed is definately not a primary concern in many circumstances (I'd refactor the comments example), using such a technique in the innermost loop of a bytecode interpreter might be justifyable in my eyes.

At least until a JIT compiler became available :-).
Quote:Original post by Conner McCloud
Quote:Original post by TANSTAAFL
Some stuff I found on the web on this topic:

http://icarus.cs.weber.edu/~dab/cs1410/chap.03/goto_statements.pdf

http://icarus.cs.weber.edu/~dab/cs1410/chap.03/comments.cpp

I have to disagree that that is a good use of goto. Maybe its just my training, but I find state machines to be infinitely more useful if you actually have states, instead of a horrible mess of jumps. If nobody else does so in the mean time, I'll rewrite that particular machine later today.

CM


I would also have an initial reaction of "that's not a good example" for both of the arguments he presents in the pdf.

I *CAN* think of a situation of goto that is useful and aids readability in the "goto as a multi-level break" scenario, if and only if:

for(size_t i=0;i<i_max;++i){    /*some code*/    for(size_t j=0;j<j_max;++j)    {        /*some code*/        if(exit_condition)        {            goto end_of_loop;        }        /*some code that you don't want to run if exit_condition is encountered*/    }    /*some code that you don't want to run if exit_condition is encountered*/}end_of_loop:


But in the case of AP's example, which did not have a nested loop, I stand by my former posts.

As for the state machine example put forth in the pdf and cpp I posted earlier... I have not attempted a non-goto version of the same code, and thus cannot speak as to it's propriety as an example. I suspect it isn't a good example, but until I can show otherwise, I won't call it "bad".

Get off my lawn!

Quote:Original post by MaulingMonkey
Quote:Original post by Conner McCloud
Quote:Original post by TANSTAAFL
Some stuff I found on the web on this topic:

http://icarus.cs.weber.edu/~dab/cs1410/chap.03/goto_statements.pdf

http://icarus.cs.weber.edu/~dab/cs1410/chap.03/comments.cpp

I have to disagree that that is a good use of goto. Maybe its just my training, but I find state machines to be infinitely more useful if you actually have states, instead of a horrible mess of jumps. If nobody else does so in the mean time, I'll rewrite that particular machine later today.

CM


While I don't think that example was a horrible mess, I too would refactor it. That said, that's probably going to be one of the fastest running parsers. While speed is definately not a primary concern in many circumstances (I'd refactor the comments example), using such a technique in the innermost loop of a bytecode interpreter might be justifyable in my eyes.

At least until a JIT compiler became available :-).


I have to wonder about someone who wants to use gotos in order to create an optimized parsing state machine, when the input is being read from a file anyway o_O

Edit: The CPP file refactored (and also reformatted in my style):

#include <iostream>using namespace std;enum { reading, beginning, cpp_comment, c_comment, ending } state;int main() {  char input;      // current input character from the program file  char lookbehind; // remembers the previous input character if needed  state s = reading;  while (cin >> input) {    switch (s) {      case reading: // start state and reading program text      if (input == '/') { lookbehind = input; s = beginning; }      else { cout << input; }      break;      case beginning: // possible beginning of a comment      switch (input) {        case '/': s = cpp_comment; break;        case '*': s = c_comment; break;        default : // wasn't a comment        cout << lookbehind << input;	s = reading;      }      break;      case cpp_comment: // C++ style comment: // to end of line      if (input == '\n') {        cerr << endl;        s = reading;      } else {        cerr << input;      }      break;      case c_comment: // C style comment (also supported by C++): /* to */      if (input = '*') {        lookbehind = input;        s = ending;      } else {        cerr << input;      }      break;      case ending: // possible end of C style comment      if (input == '/') {        cerr << endl;        s = reading;      } else { // comment didn't end        cerr << lookbehind << input;      }      break;      default:      assert(false);    }  }}


This version also simplifies the logic by reading in at only one point (by saving a "look-behind" character rather than reading in a new "look-ahead" one) and by avoiding the need to ever "goto" the current state.

[Edited by - Zahlman on March 13, 2006 7:02:37 PM]
The original poster is correct in pointing out that the Dijkstra paper was written in a time when high level language constructs didn't exist to do what people intended. For example Fortran66 doesn't have an Else keyword so you have to write:
      if(blah.eq.0) then          goto 101      endifC    This is actually the 'else bit'.      some_value = something      goto 102 101 ContinueC    This is actually the 'if'd block.      blah = array(34)  102 ContinueC ...

In today's context we all know to use the built in features that simplify this mess. I think the 5 pages from this thread and countless other threads comes from a misunderstanding of exactly how bad the situation was when Dijkstra wrote the paper. Surely nowadays everyone knows to use a loop contstuct when they want a loop and not a goto.

This is incidentally why some people like customizable languages like Lisp, Scheme, Ruby, and Smalltalk. You can define new constructs in the code so the next time you use them, you can simply say what you are doing without resorting to goto/loop/etc constructs or waiting until a new language comes out that does what you want.

Afterall, that's the point: say what you mean.
Quote:Original post by Zahlman
I have to wonder about someone who wants to use gotos in order to create an optimized parsing state machine, when the input is being read from a file anyway o_O

One hopes that the state machine is actually acting on stuff that's already in memory. Of course, I would think that such a byte-code interpreter would be sufficiently complicated that using gotos really would lead to a horrible horrible mess. Whatever the case, I'm sure most everybody would agree that if you need such heavy optimizations, the rules change significantly as to what is and is not appropriate.
Quote:Original post by Zahlman
Edit: The CPP file refactored (and also reformatted in my style):

*** Source Snippet Removed ***

This version also simplifies the logic by reading in at only one point (by saving a "look-behind" character rather than reading in a new "look-ahead" one) and by avoiding the need to ever "goto" the current state.

That's exactly how I would have done it [save the formatting, blasphemer], except I probably would have moved the case blocks into functions. For something this simple, it doesn't really matter.

flangazor: Not only are there additional constructs, goto itself is radically different in some cases. In C++, for instance, you can't jump into a different scope, only out of them. Dijkstra's paper is really only useful as historical background.

CM
"I find the G-word offensive in programming, Is there a filter to block me from see it!"

To me, using them in programming is akin to running RingSeal oil in a race car engine. Using a chainsaw to cut studs to build a house. A doctor using an xato knife to do surgery. ...

Jumps and branches are used in assembler as that is the way asm code works.
Why the G-word has not been depricated from C and Cobol is beyond me. In 25 years I've not used one in either of those languages. I have been forced to used the wretched things in VB6 for on error blocks but thanks to DotNet i'm ripping them out this month and using real try blocks. Later we will port that code to CSharp.
Quote:Original post by AlabamaCajun
Why the G-word has not been depricated from C...is beyond me.

I can't speak for Cobol, but deprecating it from C would be idiotic. Thousands of programs would suddenly start spitting out errors. Also, automatic code generators make use of goto rather heavily, so they'd all have to be rewritten. Finally, in C it serves a fairly real purpose, since the language lacks some of the high level features one could use in a different language to replace goto.

CM
That state-machine code is not practical; it runs flat-out and yields no time to anything else. It's no good for a real-time nor PC environment. With a PC you need to be event driven to minimize the amount of processing power you use. In a real-time state machine, you have to execute your state in a stable amount of time and stop on time, so that the next interrupt can come and execute the next 'tick' of the state machine.

It seemed clever at first since you don't have the "overhead" of looping and switching on a state variable, but the code is impractical for all but the most trivial cases.

...
A sub-function seems a lot better solution to me for the nested-loop-break:

some_iterator find_something(...){    for(size_t i=0; i<i_max; ++i)    {        for(size_t j=0; j<j_max; ++j)        {            if(exit_condition())            {                return make_me_an_iterator(..., i,j);            }        }    }    return make_me_an_iterator(...);}
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara

This topic is closed to new replies.

Advertisement