I'm finishinh up my game for submission. Right now I'm doing the menus, and it's highly procedural, nasty long-winded chunks of code, as you can imagine..

And I'm trying to implement the following logic as part of a huge compound switch block:
If MENU_FLAG is STATUS, then display the status menu and the mission menu;
If MENU_FLAG is DEBRIEF, then display the debrief menu and the mission menu;
If MENU_FLAG is MISSION, then display the mission menu, but no other menu;

So if it was only one dependency then I could just remove a break from one of the switch cases, but because there are two it seems to me like the easiest and most elegant solution to just use a goto statement... I mean switch blocks are just glorified gotos anyway, right? Kinda?

So:

switch(MENU_FLAG)
{
case STATUS:
// Stuff
goto mission_menu:
case DEBRIEF:
// Stuff
// No break
case MISSION:
mission_menu:
// Stuff
break;
}

Is this an elegant way to solve a problem? Or is it bad programming? This project is 8000 lines of code so I don't want to taint it with bland unprofessionalism.. not until I get you guys' advice on it anyway :)
cheers

Recommended Answers

All 21 Replies

I don't see the benefit of goto here. If your code is properly modularized, displaying the mission menu amounts to a single function call, so jumping around with goto doesn't really buy you anything. I'd conclude that you should refactor before considering a solution with goto.

while ( something ) {
    switch(MENU_FLAG)
    {
    case STATUS:
    // Stuff
    MENU_FLAG = MISSION;
    break;

    case DEBRIEF:
    // Stuff
    MENU_FLAG = MISSION;
    break;

    case MISSION:
    // Stuff
    break;
    }
}

Or

if ( MENU_FLAG == STATUS ) {
    DisplayStatusMenu();
} else if ( MENU_FLAG = DEBRIEF ) {
    DisplayDebriefMenu();
}
DisplayMissionMenu();

Just two possible ways...

There is never ever an acceptable use of goto. I know teachers who give an F for a program that uses it, and the companies I worked for absolutely forbid it.

>There is never ever an acceptable use of goto.
According to some people. The problem is that some people are blinded by the religious belief that goto is evil and simply don't see any acceptable uses. Naturally you should follow the coding guidelines of your teacher or employer, but I implore you AD, don't become one of those silly people who make absolute statements like "never use goto".

alright thanks for the advice

>I implore you AD, don't become one of those silly people who make absolute statements like "never use goto".

Your're about 20 years too late :)

>we all should consider: Edsger Wybe Dijkstra famous "Go To Statement Considered Harmful".
Indeed. We should also consider the circumstances under which that article was written. Sadly, the message of "A case against the GO TO Statement" (which was Dijkstra's chosen name for the same article) is often misinterpreted as "abolish goto" rather than actively thinking about the structure of your programs and programming for maintainability.

Also consider Knuth's report: Structured Programming with Goto Statements.

Also consider Knuth's report: Structured Programming with Goto Statements.

Also note that article was written in 1974 -- 34 years ago! C++ language hadn't even been invented yet and the first version of C++ wasn't released until 1983.

C language wasn't invented until 1971, just 3 years earlier than that 1974 Knuth article and the first version wasn't published by K&R until 1978.

So much for the creditability of that Knuth paper in modern programming.

>Also note that article was written in 1974 -- 34 years ago!
>So much for the creditability of that Knuth paper in modern programming.
If that's the case, Dijkstra's article has no credibility either. It was written in 1968 -- 40 years ago! Don't you think it's unfair to dismiss Knuth's counter-argument due to age yet allow the even older article to stand as a valid argument? I think your personal beliefs are hindering your objectivity.

>>Don't you think it's unfair to dismiss Knuth's counter-argument due to age yet allow the even older article to stand as a valid argument

Yes -- Dijkstra's article isn't relevent any more either :)

goto is in the Linux source code. That should be modern enough!

Here's a quote from Linus:

I think goto's are fine, and they are often more readable than large
amounts of indentation. That's _especially_ true if the code flow isn't
actually naturally indented (in this case it is, so I don't think using
goto is in any way _clearer_ than not, but in general goto's can be quite
good for readability).

Linus

In the spirit of reviving old threads, I may as well mention that I talked to my lecturer (and others) about it since then. He said that in a switch statement the case: labels are identical to a series of goto anchors, so... maybe just in this one instance it is acceptable?

On a side note though, I did refactor it into functions. I also ended up winning first prize in the contest. Not sure those Mircrosoft judges did check for gotos though, but I wouldn't put it past them!

Wow, I'm really surprised to see AD so boiled up over something like goto.

(I thought you were cool man.... *sniff* --- heh heh heh)

Actually, you'll notice that goto still exists in many modern languages. Why? Because it has its place. A rare one, but still useful.

All the fanaticism against it began as a backlash against poorly programmed code (often cases of "spaghetti code") and lack of structure.

Remember that underneath the pretty flowers all flow constructs are managed with gotos (or in the proper terminology, "jump" or "branch" opcodes). The purpose of higher-level constructs isn't to abolish the goto, but rather to provide a better abstraction as an end to better organized and more maintainable code, and to assist the programmer in managing closures/frames.

The problem with attacking the tool instead of the tooler (besides being "outside the box" --i.e. code-worded propaganda) is that the underlying problem is blissfully forgotten and remains undiagnosed by multitudes of programmers in their programming practices.

As a matter of interest, here is a 1996 study on coding practices as relates to the evils of goto:
http://citeseer.ist.psu.edu/148906.html
Notice that the study discovered that it doesn't take a goto to make really bad code, even spaghetti code.

Few people here have ever actually seen real spaghetti code, and therefore, predictably, lack the recondite profundity as to why it is wrong and to the depths of its evil. Anyone who has (and who doesn't hate the "structured programming revolution") knows why such virulence is directed towards the goto. But the goto isn't the root problem. It is its unfettered misuse.

Like templates? Like inheritance? Those are COMPUTED GOTOs people! ( Don't know what that is? It is almost pure evil. Take it from one who knows.) Do you like switch statements? Sure are convenient, aren't they? I also wonder what break and continue have to do with goto?

There are also nice things us functionalites like: continuations and closures which are, ahem, gotos in disguise. Functions and procedures and lambdas too... alas, the list goes on.

And while the evils associated with goto are very real, I think that the effort to destroy them by elimination have actually damaged some languages (*cough*Java*cough*).

Wonder why people like K&R, Stroustrup, and Wirth all found a place for GOTO? It is because they were all more interested in readability and simplicity. Sure, it is possible to write code that never uses goto (I do it on a regular basis), but sometimes it is the cleanest, most direct way to do something.

Here's a recent example of the most horrific evils you can think of, but it is also the cleanest solution to the problem:

//----------------------------------------------------------------------------
// get_line
//   Like std::getline(), but handles LF (unix), CRLF (windows),
//   and CR (mac) properly when handling binary streams.
//
std::string get_line( std::istream& file )
  {
  std::string result;

  while (true)
    switch (int c = file.get())
      {
      case '\r': if (file.peek() == '\n')
      case '\n':   file.get();
      case EOF:  goto l_done;
      default:   result += char( c );
      }
  l_done:

  return result;
  }

This here code both combines an if and a switch, and it uses a goto to escape. Are there ways around such things? Sure. But at the expense of size and readability, which translates to an increased likelihood of errors. I prefer the former over the latter.

As Antoine de Saint-Exupéry said [ 1 ]:

A designer knows he has achieved perfection not when there is nothing left to add, but when there is nothing left to take away.

" The apprentice uses it without thinking. The journeyman avoids it without thinking. The master uses it thoughtfully. " (emphasis added)

Don't hate GOTO. Hate bad programming. And teach newbies the difference.

My $0.03.

[edit] Wait! How old is this thread? ...Jerks. (J/K!)

commented: Very good essay +1

I dont often use goto, but it has been usefull in some cases for me too.
There might be better ways to do this but this seems like the easiest method to me:

// Breaking out of nested loops
for (int i = 0; i < 100; i++) {
   for (int j = 0; j < 100; j++) {
      for (int k = 0; k < 100; k++) {
         if (....) goto out;
      }
   }
   out:
}

>Here's a recent example of the most horrific evils you can think of, but it is also the cleanest solution to the problem
Clean is subjective. Edward would argue that instead of an infinite loop and goto, a flagged condition with a do loop is more intuitive and not any more complex:

std::string get_line(std::istream& file)
{
  std::string result;
  bool done = false;

  do {
    switch (int c = file.get()) {
      case '\r': if (file.peek() == '\n')
      case '\n':   file.get();
      case EOF:
        done = true;
        break;
      default:   result += char( c );
    }
  } while (!done);

  return result;
}

The problem with your code isn't that it used goto, it was the clever way that it used fall through in the switch. Just because it's short and elegant doesn't mean it's the best solution. Edward tries to follow the guideline that good code is boring code, because when you start doing clever things, you limit the number of people who are able to understand your code at a glance.

As a good example, if you give that code to everyone on this forum, how many do you think could accurately translate it into boring code without the switch fall through? Are you sure that there aren't any bugs without taking the algorithm apart and analyzing it more intensely than you should for 5 lines worth of switch?

So Ed prefers flaggy code to do something that is straight-forward?

Not only has she introduced additional logic to accomplish a simple break, she has overloaded the readability of the code by splitting the reader's interests between the action of the switch and the effort to escape, thereby obfuscating the relationship between the if and the switch, which is the most important consideration in the algorithm.

Further, she has added unnecessary verbiage, should that flagginess be required by one's suit.

std::string get_line(std::istream& file)
{
  std::string result;
  bool done = false;

  while (!done)
    switch (int c = file.get()) {
      case '\r':   if (file.peek() == '\n')
      case '\n':     file.get();
      case EOF:    done = true;
                   break;
      default:     result += char( c );
    }

  return result;
}

Readability and the prominence of structural relationships are paramount here.


[edit] Oh, and by the way. I don't care if "everyone" can read it. Programming requires a certain level of competence that comes from experience and time served. Those finishing a 12-week beginner's course need not apply.

The code is easy enough to trace to unroll:

while (true)
  switch (int c = file.get())
    {
    case '\r':
      if (file.peek() == '\n')
        file.get();
      return result;

    case '\n':
      file.get();
      return result;

    case EOF:
      return result;

    default:
      result += char( c );
    }

[edit] Oh, also, I'm aware that it reads two '\n'. That was part of the application.

> So Ed prefers flaggy code to do something that is straight-forward?
Ed and Duoas clearly disagree on what is straight-forward. ;)

> Not only has she introduced additional logic to accomplish a simple break
If it were a simple break, you would have been able to use a break statement instead of a goto. ;)

> Readability and the prominence of structural relationships are paramount here.
Edward has no problem with changing the indentation of the EOF case. Your way is more readable, after all. But I really think a do loop is superior here.

> Programming requires a certain level of competence that comes from experience and time served.
And maintenance tasks are usually given to entry level programmers in the working world. It doesn't matter if "everyone" can read your code, but it does matter if "Just Out of College" Steve can't read it, because that's a breeding ground for bugs.

> The code is easy enough to trace to unroll
If you know how fall through works. Edward isn't smart enough to keep all of those rules in her head, so a test program was required to be sure of what that loop was doing. Ed isn't a beginner. Do you expect rookie maintenance programmers to have an easier time of things?

> Oh, also, I'm aware that it reads two '\n'. That was part of the application.
Ed wasn't sure if that was a bug or an application requirement. What a fantastic place for a comment, don't you think. ;)

> And maintenance tasks are usually given to entry level programmers in the working world.
Hmm... good point.
My thought was just that this kind of routine ought not to need any maintenance because it is a small, do-one-thing kind of function. If other things are wanted, the correct way to add them is to overload using another function that calls the first, or simply replace it.

> If it were a simple break, you would have been able to use a break statement instead of a goto.
You can't break out of a loop from inside a switch. But you can break a loop:

while (quux)
  {
  if (baz) break;
  }

And you can break a switch:

switch (foo)
  {
  case bar: break;
  }

And break is just a goto anyway...

> Edward isn't smart enough to...
Oh come on, you're a super genius.
Fall-through is a basic property of C and C++ switch statements. Any rookie programmer who can't handle that isn't qualified for the job.
But, that doesn't change reality I guess... so good point anyway... :angry:

> What a fantastic place for a comment, don't you think.
It was commented elsewhere. (Don't duplicate commentary.) Sorry. :$

I think Ed and I are troubling over peanuts. :) Like I said, it is always possible to avoid using goto. Useful examples come so rarely it just happened to be what I had on hand.

If anyone is interested in a general purpose version (without the double newline), that's easy enough:

std::string get_line( std::istream& ins )
  {
  std::string result;

  while (true)
    switch (int c = ins.get())
      {
      case '\r': if (ins.peek() == '\n') ins.get();
      case '\n':
      case EOF:  return result;  // this here is a goto too... JSYK
      default:   result += char( c );
      }

  return result;  // unnecessary except to keep the compiler happy
  }

or:

std::string get_line( std::istream& ins )
  {
  std::string result;
  bool done = false;

  while (!done)
    switch (int c = ins.get())
      {
      case '\r': if (ins.peek() == '\n') ins.get();
      case '\n':
      case EOF:  done = true; break;
      default:   result += char( c );
      }

  return result;
  }

I'm not so sure Ed and I disagree so much as it appears. Both are really just as readable. The straight-forwardness has nothing to do with the absence or presence of a goto, but the ease with which the meaning of the algorithm can be grokked.

Thanks for the commentary. It is nice to have intelligent, equal-sided, non-abusive conversations. :cool:

Wow, I'm really surprised to see AD so boiled up over something like goto.

. . .

Don't hate GOTO. Hate bad programming. And teach newbies the difference.

Hi Duoas
You wrote an absolute great essay on that topic. I deeply appreciate it. Thank you very much.

When studying your small example it gave me a reality check that the wee goto you were using would have really solved a similar problem I had had recently what dealt with how-to-leave a cascade of if's in the middle of some for's when check fails. I set a flag what has to be rechecked when breaking the loops. Indeed, these breaks are already goto's! But with an explicit goto rechecking the flag wouldn't have been necessary anymore.

krs,
tesu

Don't take it as a release to use goto with wild abandon. :-O :S

Escaping deeply nested loops is one of the few places where it is considered "responsible gotoing".

Notice how even my silly, broken example of breaking out of a nested while/switch drew debate. Ed made good points against it, too.

But, other than that, glad you found it useful.

Just remember, readable, understandable, well organized code is the key. That only comes from an organized brain. Or, if you are like me where your brain shorts out on you at the least convenient times, well-organized notes.
:X

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.