Search for a label than a trailing right brace

  • Thread starter Thread starter rcgldr
  • Start date Start date
  • Tags Tags
    Search
AI Thread Summary
The discussion centers on the debate over the use of "goto" statements in programming, particularly in C. Some argue that searching for a label is easier than finding a trailing right brace, while others assert that proper indentation and brace notation enhance code readability. Critics of "goto" emphasize its potential to create unstructured and hard-to-follow code, advocating for loops and conditional structures instead. The conversation also touches on the use of function pointers versus state variables for managing control flow, with varying opinions on readability and maintainability. Ultimately, the thread highlights differing philosophies on code structure and the appropriateness of "goto" in programming.
  • #51


D H:

That you did, and I appreciate the effort to find relevant material. I don't recall how old the paper you provided was, however, and I really wanted to see if there's any current research in the area, or if it is generally considered to be dead and buried. In fact, I have a few minutes, let me do a quick Google search and see what we turn up:

Here's the Dijkstra paper I've mentioned:
http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html

Ironically, that's even older than the one by Knuth.

Here's another pretty basic one that's turned up:
http://portal.acm.org/citation.cfm?id=800283.811114

There are others out there, but I'm running low on time, so I think I'll let you guys take a look at some of these if you're interested. The last one, in particular, was very enjoyable.

Here's a deliciously mathematical look at how it may come to pass that such questions as whether the GOTO is an appropriate statement to use or not may some day no longer be a matter of opinion:
http://www98.griffith.edu.au/dspace/bitstream/10072/12328/1/5314.pdf
 
Technology news on Phys.org
  • #52


AUMathTutor said:
"My stated goal is to write the best code I can"
As is mine... the difference is in how we seek to achieve this goal, viz. whether or not using the goto is a good idea, ergo this discussion thread.
Then you need to explain something. You made a comment earlier:

AUMathTutor said:
in general, the GOTO should be avoided where it is feasible to do so.

which seemingly directly contradicts the claim you just made, that you seek to write the best code you can. Here, you assert that (generally speaking) goto should be avoided when feasible. You didn't say that goto should be avoided when when there are better options, you said it should be avoided when feasible.


You have made similar comments in the past:

AUMathTutor said:
I, personally, avoid the GOTO when it is not too inconvenient to do so.

Where again, you suggest that code quality is not your primary concern: instead, you avoid goto whenever sufficiently convenient.
 
  • #53


Yes, because my philosophy is to avoid mistakes which can be caused by using the GOTO by avoiding the GOTO; yours is to avoid those mistakes by simply not making them, which is a great philosophy if you can pull it off, but which is generally speaking not a satisfying solution to the problem, since if it were, there would be no problem in the first place.

This sort of reminds me of The Feynman Problem-Solving Algorithm:
(1) write down the problem;
(2) think very hard;
(3) write down the answer.

That's a great general-purpose way to go about things, too, if you can pull it off.

"You didn't say that goto should be avoided when when there are better options, you said it should be avoided when feasible."
In my way of looking at it, if other options are feasible, they are by default better options.

And was it you who said earlier that thinking about new control flow structures was not a productive activity since you can't go use them in C++? New languages happen... and when new control flow constructs or constraints are added to the language, people need to have thought about them and discussed the need for them in future languages.

And what about declaring blocks of code as GOTO if they use GOTOs, loops as BREAK or CONTINUE if they use BREAK or CONTINUE, and MULTIPLE ENTRY / EXIT if they contain multiple points of ENTRY / EXIT? This seems like a good idea to me. I think that in C# you can do the same thing to use pointers, which I think is a great idea.
 
Last edited:
  • #54


AUMathTutor said:
Yes, because my philosophy is to avoid mistakes which can be caused by using the goto by avoiding the goto; yours is to avoid those mistakes by simply not making them
What are "mistakes" that can be caused by using goto? I've never seen a list of these anywhere. How difficult would be it be to avoid these mistakes? In my examples where I did use goto, would you find it difficult to understand or maintain the code where I used goto's?

Why is goto the only statement to be avoided? Couldn't the same argment be made for any statement type of a language? What mistakes can goto cause that couldn't also be caused with poor usage of break, continue, if+else, ... ? Should we also ban the usage of pointers because of the risk of mistake? Should be ban recusion because it can be coded around?
 
  • #55


Mistake may not have been the best word for it. I'll think about exactly how I want to say this bit before I get back to you.

As far as the other part of your post, I agree that other constructs are just as bad as the GOTO. The GOTO is sort of a scapegoat. However, I do believe that some constructs are inherently "better" than others.

I feel that the more limited in scope and more rigid in behavior a construct, the better. Why? Because if people are familar with what a construct is capable of accomplishing, it is easier to understand code which uses this construct. Naturally, this necessitates a larger set of constructs than may otherwise be required. The GOTO is sufficient to simulate the IF-THEN-ELSE, the SWITCH, the WHILE, the FOR... you name it, the GOTO can do it. So why have any constructs at all?

We know how to reason about if-then-else constructs, while loops, and for loops from mathematics. These constructs correspond to very familiar ideas in mathematics... they are, in a sense, referentially transparent in that their semantics do not depend on their context. This makes it a lot easier to reason formally about what a program is actually doing.
 
  • #56


Maybe it would help to mention my restrictions for the usage of goto. I didn't make it clear that I limit the usage of goto's to two situations, only providing examples instead of stating restrictions on usage.

1. I use goto's for exception or failure cases, so the main code flow deals with the normal case, and without "undeeded" indentation.

2. I use goto's for multiple exit paths out of nested code, but these situations are relatively rare. In languages where this could cause problems, compilers will usually issue errors or at least warnings. I'll prefix such sections of code with a comment regarding any breakout gotos, a warning similar to the try clause of a try throw catch sequence.

One annoying (to me) situation is when reacing the end of a nested section represents failure. Due to my rule #1, no significant exception handling in the main line of code, I end up using another goto just after the nested sequence to deal with the exception, followed by a "success" label to continue the main line code.

In other cases, the early or final exits are simply alternate conditions. For example when building and using a dictionary, it's expected to not find a match, so the two main paths are create new entry and/or use existing entry.
 
Last edited:
  • #57


AUMathTutor said:
Yes, because my philosophy is to avoid mistakes which can be caused by using the GOTO by avoiding the GOTO;
So it's more like buying insurance then. You (may) pay a price on every piece of code you write -- your will write poorer code than you could have in any situation where using goto, break, multiple returns, et cetera would be an improvement -- to help protect against a particular kind of problem.

Is this a fair characterization? If so, it really is misleading to discuss alternatives to goto as if they are better pieces of code...


And was it you who said earlier that thinking about new control flow structures was not a productive activity since you can't go use them in C++?
I am the one you're thinking about, but you have the intent wrong -- the point was that it's not fair to criticize a particular implementation on the grounds that it really should the "condexpr" construct when it doesn't actually exist.
 
  • #58


"your will write poorer code than you could have in any situation where using goto, break, multiple returns, et cetera would be an improvement"

By my definition of quality, surely you realize that - almost by definition - the fewer unconditional jumps, the better. Exceptions to the rule can certainly be allowed, but only when the alternatives aren't feasible - that is, to do it another way won't work for some reason. I won't agree that my philosophy is to write poor-quality code just to dogmatically avoid a language construct, because that's not my intent.
 
  • #59


AUMathTutor said:
unconditional jumps
Unconditional jumps aren't the issue, or at least not mine. All the exampes of the goto's and discussion here have been about using goto's for conditional branching.

Unconditional goto's might be used to implement the equivalent of a while(1){...} in a language that didn't suport it, where the exit point is in the middle of a loop instead of at either end, but this wasn't brought up until now.
 
  • #60


AUMathTutor said:
I won't agree that my philosophy is to write poor-quality code just to dogmatically avoid a language construct, because that's not my intent.
You (practically) define goto to be bad. How is that not dogmatic?

Anyways, your definition of "quality" is irrelevant to me -- I'm interested in things like correctness, readability, portability, efficiency, reusability, rather than in syntactic games -- so I'll stop discussing it.
 
  • #61


"You (practically) define goto to be bad. How is that not dogmatic?"
Please. I have said over and over again that my position is to avoid the goto when feasible, and to use it only when necessary. Feasability is a broad term. In high-level user-interface programming, where efficiency is not an issue and the code is not complicated, the goto should never have to be used. For lower level code where efficiency is an issue, the use of the goto can become appropriate (although, like I said, none of the examples provided so far in this discussion thread seem very compelling to me).

Is it dogmatic to have principles which guide programming practice? If so, I'm dogmatic, and so are all good programmers. You need a consistent system... I would say it's better to be consistent in the development of software than to just use whatever you think (in passing) might be right for the job.

"I'm interested in things like correctness, readability, portability, efficiency, reusability, rather than in syntactic games"
Wow... that's what we've been talking about all along. Are you following any of this? Your opinions really don't mean much to me, since your only justification for using goto is that it's easy, and your defense of this decision to use unstructured programming techniques whenever you feel like it is that it "improves quality", supposing the conclusion. Several papers have been linked to which discuss issues such as the GOTO... perhaps you should recommend some of the ones you've read, or perhaps you should find and read some.

"so I'll stop discussing it."
Or, instead of trying to make a meaningful contribution to anything, you can pretend you're better than the rest of us, and stock off like a child. Whatever.
 
  • #62


As you are still a student (see [post=2172341]this post[/post]), AuMathTutor, you are simply not qualified to discuss this topic. You most likely have not discovered the true horrors of project managers who never yield on the programming rules they have established. You have not discovered the wonders of hacked-and-slashed legacy code (but hacked-and-slashed in accordance per programming rules) that has turned into a monster as bad as any code from the 1960s. See [post=2169275]post #21[/post].

You have been indoctrinated and are being incredibly dogmatic about your indoctrination. Hurkyl wants to write the best code possible, where "best" to Hurkyl means extensible, reusable, maintainable, understandable, etc. Avoiding GOTOs where a higher level construct would serve better is one means of achieving this goal. Using a GOTO where a higher level construct would reduce the quality of the code is also a means of achieving this goal. Hurkyl is being pragmatic.

Your concept of the "best" is anything without a GOTO. You have elevated avoidance of GOTOs to a goal in an of itself. That is dogma.
 
  • #63


No need for ad hominems.

I've yet to see any situation where a "goto" does a good job and could not be replaced with modern programming constructs. The "principle of least power" is at work here. goto is simply a tool of uncontrolled power; a goto can produce a huge range of behaviors depending on the destination of the goto. If you have two code constructs which accomplish what you need, but one of them is more powerful, usually go with the weaker one; more power means more ways the code could become complicated and hard to understand, and also more chances to introduce bugs. Another way to put it is, too much power introduces unintended consequences. The principle of least power underlies several good programming maxims, in addition to avoiding goto:
  • the idea that a function should have a clearly defined purpose, doing only one thing. This follows because functions that do multiple things are more powerful, and therefore have more potential to be misused or buggy.
  • the idea that functions should be referentially transparent (when possible) or at least not access many global variables. A function that is not referentially transparent has less constrained behavior, thus it is more powerful (and thus can have more unintended consequences).
  • the idea that values should have types. An untyped value has greater potential to be misused. It could be the number 3, the string "suzy", or an instance of a mysterious class. Thus it is less constrained than a typed value, and more powerful.
Power means lack of constraints. The principle of least power can alternately be stated as, given a choice between a way of doing things which is constrained, and a way of doing things which is less constrained, choose the more constrained one.

On the other hand, generality is a form of power that is "good." For example, if you have a function "snd" that extracts the second element of an ordered pair, this is more general than a function "sndInt" that extracts the second element of an ordered pair of ints. snd is less constrained and more powerful, but it is probably better because it has wider applicability. What can be taken from this is, things should be as general as they need to be, but they should avoid any kind of generality that is not convincingly justified.
 
Last edited:
  • #64


"As you are still a student (see this post), AuMathTutor, you are simply not qualified to discuss this topic."
I hope your software isn't as flawed as your logic. Let's stick to the issues, D H. I could just as easily say that your experience has corrupted you and made you a biased observer, incapable of seeing both sides of the issue.

"You have been indoctrinated and are being incredibly dogmatic about your indoctrination."
Would you please cut out the pretending like you know me? Experience doesn't count for **** if you can't even come up with a compelling case for your position... much less if you can't even understand the difference between avoiding the GOTO to support good programming and "elevating avoidance of GOTOs to a goal in an of itself.".

I think it's healthy to feel a little nervous when the GOTO starts showing up over and over again in your code. However you slice it, our opinions differ only in degree, not in kind... you just avoid the GOTO less, and I avoid it more. Otherwise you wouldn't need any special control flow constructs at all.

That's all the response I'm going to give to your post, D H, because it's really not worth anything and contributes nothing to the discussion. If you want this to turn into an ad-hominem flame war, by all means, let's go.
 
  • #65


mXCSNT:

I like the idea that GOTO is too powerful to admit general, default use of it when it seems like it could get the job done.

For me, it's sort of like information hiding. The GOTO is sort of like global variables... alright in small doses and with justification, but that doesn't mean it's alright to start making every other variable global in scope. The number of possible interactions grows too quickly to be scalable in larger applications.

You know, in a way, I think that functional programming languages are probably the best idea, in terms of writing good code. They force you (or at least strongly suggest to you) to abandon state altogether... without state, you gain referential transparency and guarantee control flow in very clear terms (usually). In fact, if it weren't for the fact that they're less efficient than imperative languages, I'd probably advocate using them. The syntax is easy, the semantics are easy, and it sidesteps all the problems of global variables, gotos, etc. I wonder if in a parallel universe there's a group of people arguing on physics forums about some feature of functional programming languages which should or shouldn't be used for functional programming.
 
  • #66


AUMathTutor said:
Would you please cut out the pretending like you know me?
He may not know you, but he knows the persona you present through your posts...

much less if you can't even understand the difference between avoiding the GOTO to support good programming and "elevating avoidance of GOTOs to a goal in an of itself.".
I know the difference. Are you sure you do?

Suppose, for the sake of argument, you have two pieces of code, X and Y. X uses goto's. Y does not. Y is more readable than X. X is more error-prone under modification than Y. All other qualitative aspects are equal.

We can all agree, I believe, that Y is a better piece of code than X.

Question: consider the statement "X uses goto, Y doesn't, therefore, Y is better". Is this an example of
(1) "avoiding goto's to support good programming"
or is it an example of
(2) "elevating avoidance of goto's to a goal in of itself"
?

Now, answer the same question regarding the statement "Y demonstrates that avoiding goto is feasible, and therefore, you shouldn't use X".
 
  • #67


Sure, it's possible that good code uses gotos or that bad code has no gotos. I don't think anyone here would disagree with that.
 
  • #68


"He may not know you, but he knows the persona you present through your posts..."
Not that it makes his argument valid, or that it should factor into any discussion, or that it makes any difference at all to the issues we're discussing what sort of persona I present, but alright, sure.

"I know the difference. Are you sure you do?"
I'm pretty sure I do. I'm glad you think you do too.

"Suppose, for the sake of argument, you have two pieces of code, X and Y. X uses goto's. Y does not. Y is more readable than X. X is more error-prone under modification than Y. All other qualitative aspects are equal.

We can all agree, I believe, that Y is a better piece of code than X."
Granted...

"Question: consider the statement "X uses goto, Y doesn't, therefore, Y is better". Is this an example of
(1) "avoiding goto's to support good programming"
or is it an example of
(2) "elevating avoidance of goto's to a goal in of itself""
I think this demonstrates that you have a fundamentally distorted view of my position with respect to the GOTO. Yes, if two otherwise identical pieces of code are being compared, and the only difference is that one uses the GOTO and one doesn't, I would select the one without the GOTO every time. If all things are not equal, then I may select the one containing GOTOs under special scenarios if *I must do so*. In my (limited, as pointed out by D H) experience, the GOTO is not really ever neededfor high-level programming. For most usual operations, including all the ones brought up here, I'd sooner resort to structured techniques, including state variables, if-then-else blocks, and loops. If I don't need it, I don't use it.

"Now, answer the same question regarding the statement "Y demonstrates that avoiding goto is feasible, and therefore, you shouldn't use X"."
Depending on exactly what you mean by feasible, then yes, I agree with this statement in every case I can imagine. However, when I think of feasibility, I think of it in terms of being an appropriate solution... not just any solution. Maybe this is a non-standard usage of the word, and if I caused a misunderstanding by using it, I apologize.
 
  • #69


"Sure, it's possible that good code uses gotos or that bad code has no gotos. I don't think anyone here would disagree with that."

It's not a matter of good or bad... it's a matter of degrees. I, for one, believe that it's a good idea to avoid the GOTO, but that's not to say that GOTOs cannot be used correctly. I just feel that it's all too easy to misuse them, and that what may seem like an appropriate use to one person may not be so to another. Most constructs have the property that when you look at it, you can tell what's going to happen next. I don't think the GOTO necessarily possesses this property.

But yes, all control constructs are based on the GOTO, so GOTO code can be alright. But in my way of looking at it, most things you need to do can be done without GOTO... unless you're doing something that really merits their use, like as in specifically the GOTO and not some other construct.
 
  • #70


The part I don't understand is why "goto" gets so much attention? What about the triple branch in fortran "if(expression)a,b,c where a,b,c are labels branched to if the expression is negative, zero, or positive, since it requires the usage of labels, and there is no execution path that follows such an if, it's the termination of a code sequence (always branches). Why pick on just "goto"? Are there any other constructs in C that we should consider abolishing?

Regarding avoiding a particular construct of a language, I recall a real world disaster on a project. The team was using an old version of Pascal that typed all 8 bit values as characters and didn't allow any math operations on them. This project was character intensive, (data entry system), and functions similar to converting to all caps were common. The compiler could have been fixed to allow 8 bit integers, but the "gurus" on this team argued that not allowing math on 8 bit values would prevent programmers from making the mistake of accidentally doing math with 8 bit values, and preventing this "mistake" was worth the penalty of having to move strings or arrays of characters to strings or arrays of integers and back in order to do numerical operations, even though the code was messier and performance degraded (to the point that the project failed to meet it's peformance requirements and was canceled).
 
  • #71


I don't know fortran, but it sounds like the triple branch is worse than goto. I guess it gets less attention because it's not present in as many languages.
 
  • #72


"I don't know fortran, but it sounds like the triple branch is worse than goto. I guess it gets less attention because it's not present in as many languages."

Agreed.
 
  • #73


People do pick on Fortran's triple branch and have done so even before FORTRAN became Fortran. Fortran 90 marked it as obsolete, and compilers can be made to issue warnings about use of any features marked for obsolescence.

C's (and C++'s) switch statement is essentially a computed goto. This enables constructs such as Duff's device, here used to copy count items from array from to some special memory location to:
Code:
switch (count % 8) {
  case 0: do {  *to = *from++;
  case 7:       *to = *from++;
  case 6:       *to = *from++;
  case 5:       *to = *from++;
  case 4:       *to = *from++;
  case 3:       *to = *from++;
  case 2:       *to = *from++;
  case 1:       *to = *from++;
           } while ((count -= 8) > 0);
}

Note however, that there are no gotos here!

Speaking of switch statements, I've always found the default fall-through behavior a bit annoying and bug-inducing. Most languages that have a switch statement analog do not have this behavior; they don't even have a mechanism to support fall through.
 
Last edited:
  • #74


D H said:
C's (and C++'s) switch statement is essentially a computed goto. This enables constructs such as Duff's device.
I had forgotten switch case can be used to as means to implement multiple entry points into nested code.
 
Last edited:
  • #75


Switch is more limited, and therefore safer than goto: all of the labels that flow can jump to have to lie within the switch block.

Still, many an unwary programmer has left out a necessary "break" statement. On a small scale, that illustrates a peril of goto-structured code: forgetting to jump to the beginning of the next block at the end of the current one.
 
Last edited:
  • #76


Also, jumps are only forward jumps. These two things taken together make it fairly straightforward to understand and harder to abuse.

I agree, however, that the fall-through is a bad idea in general. I think it's for the best that most newer languages don't allow this.
 
  • #77


Duff's Device is abuse personified. I would rate Duff's Device as much worse in terms of software quality than a goto used for a multi-level break or for error handling. But hey! Duff's Device doesn't have any gotos. It must be OK.

Finding all of the abusive but goto-free practices in a software package can be difficult. Static analysis tools can find some, but making them do so also tends to make them produce a lot false positives. Finding all of the errors in a software package is even harder; for a package of any size is practically impossible. The goal of testing is to reduce the errors to some acceptable level. Even then, a very significant amount of money, well over half of the total project budget for safety-critical software, is spent on testing.

In comparison, finding all the gotos in a software package is easy:
grep -l goto `find $PACKAGE_HOME -name '*.cc'`
There shouldn't be very many. That does not mean there shouldn't be any. A well-used goto can significantly the reduce accidental complexity of the function in which it is used, and may well reduce the accidental complexity of project as a whole.

Those of you who advocate no gotos whatsoever are raising a phony argument. Nobody here is advocating a return to 1960s-style spaghetti code. Djikstra was fighting indiscriminate use of gotos. Gotos are not used indiscriminately now. They are used on a very discriminating basis, if for no other reason that many quality software projects that do allow the use of gotos only allow this use after granting a waiver.

Good programmers learn to look for design patterns, things that can be reused. Excellent programmers learn to look for anti-patterns, things that should be avoided. The key problem with dogmatically rejecting goto, break, and multiple return statements is that doing so creates an anti-pattern, the Arrow Anti-Pattern. Use of the arrow anti-pattern increases the accidental complexity of the code. As errors are highly correlated with code complexity, increasing the accidental complexity is a bad thing to do.


Some reading material:
  • Steve McConnell, "Code Complete: A Practical Handbook of Software Construction", Section 16.1, Microsoft Press, 1993
    http://www.stevemcconnell.com/ccgoto.htm

    This book is a must-read for anyone who wants to progress far in the realm of computer programming and was written by one of the three most influential people in the software industry. The above link is an extract from this book on the use of the goto statement. Even though McConnell comes from the software engineering community, he says "In the remaining one case out of 100 in which a goto is a legitimate solution to the problem, document it clearly and use it."


  • Frederick Brooks, "No Silver Bullet Essence and Accidents of Software Engineering", Computer, 20:4, 1987
    http://www.lips.utexas.edu/ee382c-15005/Readings/Readings1/05-Broo87.pdf

    This paper is the origin of the terms "accidental complexity" and "essential complexity". You can find this paper in Brooks' book "The Mythical Man-Month", another must read.


  • Jeff Atwood, "Flattening Arrow Code", Coding Horror (blog), 2006
    http://www.codinghorror.com/blog/archives/000486.html
    Atwood's Coding Horror blog said:
    The excessive nesting of conditional clauses pushes the code out into an arrow formation:

    Code:
     if
       if
         if
           if
             do something
           endif
         endif
       endif
     endif

    And you know you're definitely in trouble when the code you're reading is regularly exceeding the right margin on a typical 1280x1024 display. This is the http://c2.com/cgi/wiki?ArrowAntiPattern" in action.


  • lucky.linux.kernel, Google Groups, 2003
    http://kerneltrap.org/node/553/2131
    NewsGroup said:
    From: Linus Torvalds
    Subject: Re: any chance of 2.6.0-test*?
    Date: Sun, 12 Jan 2003 12:22:26 -0800 (PST)

    On Sun, 12 Jan 2003, Rob Wilkens wrote:
    >
    > However, I have always been taught, and have always believed that
    > "goto"s are inherently evil. They are the creators of spaghetti code

    No, you've been brainwashed by CS people who thought that Niklaus Wirth
    actually knew what he was talking about. He didn't. He doesn't have a
    frigging clue.
 
Last edited by a moderator:
  • #78


D H said:
Steve McConnell, "Code Complete: A Practical Handbook of Software Construction"
Wow, that brought back memories. At a previous job, a co-worker doing a code review commented in a email to the group on my use of a single instance of if(...){goto} in a very simple module. The group boss, who would occasionally publicly chastise group members with group emails, responded back with a group email that I should refer to Code Complete, noting he had bought a copy for each member of the group, and that I should follow it's guidelines. In what was a potentially career limiting move I pointed out that, except for the "goto goons", the usage of goto in such cases was acceptable and that the arrow anti-pattern of deep nesting of if else, the one that the group boss himself was pushing, was the only method considered unacceptable by that book. The response after that was that the book was just a general guide.

This example of arrow anti pattern from the link above isn't much different from actual code sequences used by the "compliant" members of that group:

Code:
if get_resource
   if get_resource
     if get_resource
       if get_resource
         do something
         free_resource
       else
         error_path
       endif
       free_resource
     else
       error_path
     endif
     free_resource
   else
     error_path
   endif
   free_resource 
 else
   error_path
 endif
 
Last edited:
  • #79


AUMathTutor said:
Also, jumps [in a switch statement] are only forward jumps.
You may have a point there. Every claimed instance of a "good" goto involves a forward jump, never a backwards jump. Perhaps a goto limited to forward jumps only would not be as risky. Even better might be something like "gonext" which only jumps forward, and only jumps to the nearest following label named "next" (so that you can't interlace two jumps). Something like
Code:
for(int i=0;i<10;i++)
  for(int j=0;j<10;j++){
    condition = do_something(i,j);
    if(condition == ERR)
      gonext;
  }
next:
...
Such a "gonext" jump could not be abused in the ways that goto can.

If we're talking about speculative alternatives to goto, how about this one: a "smashable" block that can be broken out of by a "smash" statement. So the above would be equivalent to the below:
Code:
smashable{
  for(int i=0;i<10;i++)
    for(int j=0;j<10;j++){
      condition = do_something(i,j);
      if(condition == ERR)
        smash;
    }
}
...
This approach would be completely consistent with structured programming.

D H, I agree that Duff's device is not clean code. Is it abuse? It has an upside, namely performance. Perhaps in a few applications, it is necessary. Inline assembly code is also more complicated and less clean than straight C, but sometimes justified for performance reasons.

As we've already talked about (beginning of this discussion), there are ways to get around the "arrow pattern." Here's another, albeit peculiar:
Code:
switch(0){
case 0:
  condition = do_something();
  if(condition == ERR)
    break;
  condition = something_else();
  if(condition == ERR)
    break;
  ...
}
I'm not recommending this, because it is weird code, and weird means confusing. There are better ways to solve the problem, already discussed (throw an error exception or return from the function). But even though it is strange, the above code is arguably superior to using gotos, because unlike goto you don't have to read all the way down to the bottom to find the point that the "break" takes you to. You know, upon reading the break, that you're jumping out of the enclosing switch.
 
  • #80


D H:

"Duff's Device is abuse personified. I would rate Duff's Device as much worse in terms of software quality than a goto used for a multi-level break or for error handling. But hey! Duff's Device doesn't have any gotos. It must be OK."
Wow, your logic is awesome.

X is bad.
Y does not contain X.
Y is good.

I hope that after years of experience my understanding of basic logic is still as well-developed as yours.

"They are used on a very discriminating basis, if for no other reason that many quality software projects that do allow the use of gotos only allow this use after granting a waiver"
Well that's reassuring. Perhaps if you had been paying attention to my posts, you would have seen that I have said this should be what is done, over, and over, and over, and over, and over, again. I guess the world isn't such an insane place after all.

Jeff Reid:

It seems like the only argument against the arrow anti-pattern is that it pushes code out to the right when there are more than two nested ifs. While I don't think this is really too big a problem... I mean, so you have to scroll a little... I guess it is sort of a pain and not too aesthetically pleasing either. I'll think of another way to handle the above... I'm thinking a resource counter and a loop or two.
 
  • #81


And I really like your idea of gotoNext. If it weren't for interlacing GOTOs and backwards GOTOs, I'd have much less of a problem with them.
 
  • #82


Stop with the histrionics, AuMathTutor.

The argument against the arrow anti-pattern is that cyclomatic complexity and the presence of software errors are highly correlated. A software item that exhibits this arrow pattern is strongly indicative of that the code is buggy.
 
  • #83


"Stop with the histrionics, AuMathTutor."
I'll stop with the histrionics if you cut out the ad hominems and the blatant misrepresentations of my positions.

And as far as the arrow anti-pattern, I think there are alternatives that don't use the GOTO. Just because something takes some thought to figure out doesn't mean the thought isn't worth the effort.
 
  • #84


D H said:
The argument against the arrow anti-pattern is that cyclomatic complexity and the presence of software errors are highly correlated. A software item that exhibits this arrow pattern is strongly indicative of that the code is buggy.
The arrow anti-pattern has the same cyclomatic complexity as the equivalent code rewritten with gotos. The number of "if" statements is probably not the point--maybe there is a way to rewrite the code without so many tests, which would definitely be good if possible, but it's also likely that the conditionals test conditions that have to be tested (irreducibly complicated). That alone doesn't make it an anti-pattern. It's an anti-pattern because the excessive indentation makes it hard to read, and cleanup code may end up far from the resource allocation, as http://c2.com/cgi/wiki?ArrowAntiPattern points out.
 
  • #85


Jumping forward to nearest label would seem to be a bad idea, because the target of the jump could secretly change depending on what the intervening code contains. (In particular, if it contained another gonext .. label pair)


Since the "don't goto backwards" thing keeps coming up, I'll mention the two cases I thought of where they might be useful. (This isn't a fully-formed thought, just the germs of ideas)

The first involves two partially overlapping logical blocks of code. e.g. you might want to implement

Code:
do {
  // Some code
  // Other logical block starts here
  // Some code
} while(condition);
// Some code
// Other logical block ends here

For example, the second logical block might be an atomic access to some resource, and other concerns might strongly suggest, or even strictly require, that that block really and truly be a block, and therefore you cannot use while to express the loop. You could retain the structure directly, e.g. in a hypothetical java + goto language, by

Code:
top_of_loop:
  // Some code
  synchronized(resource) {
    // Some code
    if(condition) { goto top_of_loop; }
    // Some code
  }

But other solutions (in languages I know) would require either using things in a strange way

Code:
  while(true) {
    // Some code
    synchronized(resource) {
      // Some code
      if(condition) { continue; }
      // Some code
    }
    break;
  }

or abandoning the loop all together and writing some more complicated structure that works around the block.


The other example is complementary to the exception handling case -- upon a failure, rather than skipping ahead to the end of function to report failure, they skip backwards to try again.


Of course, there is also the case where you really are implementing an unstructured algorithm (e.g. a state machine automatically generated to do parsing), so any method of implementation is going to have an unstructured pattern to it, and the question is whether the state machine is described in C (and thus needing gotos in all directions, or while true & switch & break to emulate the goto) or if the state machine is described in some other manner amenable to whatever state machine object you've written.
 
  • #86


You're right, unintentionally changing the destination would be a fatal flaw in gonext. The "smashable" block is a better solution (a smashable block could be nested within another smashable block, and a smash statement would only get you out of the nearest enclosing one).

I suppose, if you wanted to actually use gonext, you could make two gonext's in a row without an intervening "next" tag be a syntax error. This would prevent the accidental introduction of a gonext/next pair between another gonext/next pair.

As you've shown, loops work okay for the situations you're describing. The goto is unnecessary. My personal style for a "retry until complete" situation is to use a "finished" flag, like:

Code:
  finished = false;
  while(!finished) {
    // Some code
    synchronized(resource) {
      // Some code
      if(condition) { continue; }
      finished = true;
      // Some code
    }
  }

This doesn't significantly change the structure, but perhaps it makes it a little clearer what I'm doing.

In situations where you have unstructured, automatically generated code, it doesn't matter what it looks like--unrestricted use of goto is fine in that situation (so long as the code is correct and efficient) because it's not intended for humans.
 
Last edited:
  • #87


AUMathTutor said:
It seems like the only argument against the arrow anti-pattern is that it pushes code out to the right when there are more than two nested ifs.
That's only part of the issue, the main issue is that it the else handling for the outer ifs are farthest away from the code fragment that triggered the else condition. From Code Complete: Moreover, the distance between error-processing code and code that invokes it is too far: the code that sets ErrorState to FileFindError, for example, is 23 lines from the if statement that invokes it.[/quote]

Guarding of code via a flag can be used, to avoid gotos:

Code:
    fgotoxyz = FALSE
    if(fgotoxyz == FALSE)
    {
        // step 1
        if(step 1 failed)
        {
           step 1 error handling;
            fgotoend = TRUE;
        }
    }

    if(fgotoxyz == FALSE)
    {
        // step 2
        if(step 2 failed)
        {
            step 2 error handling;
            fgotoend = TRUE;
        }
    }
    ...
    // xyz section starts here

or switch breaks to perform the same function as goto

Code:
    switch(0)
    {
      case 0:
        //step 1
        if(step 1 failed)
        {
            step 1 error handling
            break;
        }
        //step 2
        if(step 2 failed)
        {
            step 2 error handling
            break;
        }
         ...
    }
 
  • #88


mXSCNT said:
The arrow anti-pattern has the same cyclomatic complexity as the equivalent code rewritten with gotos.

No, it doesn't. Read post #21 for a counterexample.

Converting old-fashioned spaghetti code to something to a pure structured programming form requires the creation of extra variables, extra boolean clauses, and at times, replication of code. For sufficiently large N, there exists an N-statement program written with gotos that requires N! statements when without any gotos. Those extra boolean clauses and the replicated code act to increase static analysis metrics such as complexity.

The statement if (a && b) do_something(); can be rewritten as if (a) if (b) do_something(); The two forms are functionally equivalent but have different cyclomatic complexity (2 versus 3). They do however have the same extended cyclomatic complexity (3). Another way to look at it: To achieve 100% code coverage using a good code coverage tool one would have to write three test cases for this do_something() code snippet, regardless of the form in which it is written.mXSCNT, what you and AuMathTutor are doing is creating phony arguments against the use of goto. I posted a link to a section from Code Complete in post #77. Did you read it? That a five line code snippet that can be written without using gotos is a phone argument against gotos. Where the use of goto is justified is in halving the lines of code and decimating the complexity of a 800+ line monstrosity with a cyclomatic complexity of 96.

In my experience, gotos are most likely to be used in projects where some fool of a project manager has mandated the atrocious (IMHO) "single return" anti-pattern and has ruled out use of break and continue to boot. These rules practically mandate the use of gotos in complex functions where guards must be placed against errors.

One way around the error guard use of gotos is to use exception handling, if available. There are two problems with this. One is that using the exception handler for expected behaviors (e.g., EOF; every file has an EOF, so hitting EOF is not an exceptional thing to do) is itself an anti-pattern, the "Expection Anti-Pattern". Another problem is that in real-time programming, the words try, throw, and catch are made illegal. (Exceptions are slow, which alone wreaks havoc with many real-time scheduling algorithms. That exceptions typically involve hopping in and out of kernel mode wreaks serious havoc with scheduling algorithms.)

The easiest way to avoid having any gotos is to allow the use of the return, break and continue statements. Example: A common requirement in the simulation world is that behavior models must be disable-able and must not perform any computations (other than testing for being disabled) if they are disabled. I would much prefer to see if (disabled) return; at the very start of a model implementation than any other construct. Verification of this requirement is then achieved by a simple code inspection that takes a few seconds.
 
  • #89


Post #21 is an anecdote of a poorly managed project. It has no bearing on my point, which is that a straightforward translation of the arrow anti-pattern to use gotos instead, does not change the cyclomatic complexity of the code. The paths through the code are all essentially the same, just denoted differently.
For sufficiently large N, there exists an N-statement program written with gotos that requires N! statements when without any gotos.
How do you count the number of "statements required"? Any program could be written in a bounded number of statements, simply by using that bounded number of statements to write an interpreter, then supplying the rest of the program as a string constant argument to the interpreter.

We do agree that banning multiple points of return and break/continue statements can harm the legibility of code.
 
  • #90


That an N statement perniciously written pile of spaghetti might expand to N! statements of neatly written structured program should not be too surprising. This rewriting of the code is essentially the general Boolean satisfiability problem, which is of course an NP-complete problem.
 
  • #91


D H said:
That an N statement perniciously written pile of spaghetti might expand to N! statements of neatly written structured program should not be too surprising. This rewriting of the code is essentially the general Boolean satisfiability problem, which is of course an NP-complete problem.

You are not making any sense to me. How about you make things a bit more formal?
 
Back
Top