Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Search for a label than a trailing right brace

  1. Apr 20, 2009 #1

    rcgldr

    User Avatar
    Homework Helper

    Rather than hijack the help with C thread, discussion can continue here.

    Just like anything else, goto's can be used appropriately or inappropriately. Inappropriate use doesn't invalidate using goto's.

    I assume you mean one main loop per function, not per program. Take the case of a file copy or backup program that processes all the files in a directory before working on any subdirectories. There are two main loops, one to process all files in the current directory, and one to process all the sub-directories, recursively calling the process files loop, which in turn recursively calls the process sub-directories loop, ...

    On a somewhat related issue, I prefer to use pointer to functions as opposed to state variables (switch case) for deferred actions, as it allows a series of small functions to be sequentially located in a source file rather than tied into the switch case statement for some common message handler. If I need to add a step, I don't have to edit the common message handler as it just calls a pointer to function. C++ implements the equivalent in it's classes, for C, structures can have pointers to functions.

     
    Last edited: Apr 20, 2009
  2. jcsd
  3. Apr 20, 2009 #2

    rcgldr

    User Avatar
    Homework Helper

    Re: goto

    Depends on the nature of the failure in the previous steps. I could prefix each step with an if "no hardware failure detected", and that would not imply dependency among the steps. With any sequential process, each step depends on the output of a previous step, and in my coding style I don't indent the steps of a sequential process.

    Except for truly parallel sequences (multiple parallel flow charts) I understand what you're getting at, but it can get to a point that using state variables or the equivalent to avoid sequential loops is more unreadable than simply coding several sequential loops. Likewise, sometimes recursion is more readable than creating a virtual stack of state variables to accomplish the equivalent.

    I try to avoid making the same decision twice. If the action on a decision needs to be deferred, than I use some type of programmed jump or call instead of retesting a state variable. In the case of Fortran, this can be accomplished via a computed "goto" (I haven't written a Fortran program since the 1970's). In the case of C, it can be accomplished by calling pointer to a function. In C++, a class member function can be overridden. There are times when state variables are better, but I prefer using pointers to functions. Using if statements or switch case statements to simply avoid the few cases where a goto is probably the best choice, isn't a good idea.

    Example of this below. Note that adding a step doesn't require any change to the common handler source:

    Code (Text):

    //  common handler

    CommonHandler(void)
    {
        switch (message){
            ...
            case eNextStep:
                *(pfNextStep)();
                break;
            ...
        }
    }

    Step1(void)
    {
        pfNextStep = step2;
        // initiate step1 sequence
    }

    Step2(void)
    {
        pfNextStep = step3;
        // initiate step2 sequence
    }

    Step3(void)
    {
        pfNextStep = SequenceDone;
        // initiate step3 sequence
    }

    SequenceDone(void)
    {
        pfNextStep = NextStepUnexpected;
    }

    NextStepUnexpected(void)
    {
        // handled unexpected next step message
    }
     
     
    Last edited: Apr 21, 2009
  4. Apr 21, 2009 #3
    Re: goto

    "Depends on the nature of the failure in the previous steps. I could prefix each step with an if "no hardware failure detected", and that would not imply dependency among the steps. With any sequential process, each step depends on the output of a previous step, and in my coding style I don't indent the steps of a sequential process."

    Again, I don't think it's right to think of the if statements as being on a single level. The results may be independent of previous steps, but the preconditions and postconditions are intimately connected. In words, you would say:

    do step 1.
    if step 1 worked, do step 2.
    if step 2 worked, do step 3.
    if step 3 worked, do step 4.

    These can be logically expanded to the following:

    do step 1.
    if step 1 worked, do step 2.
    if step 1 and step 2 worked, do step 3.
    if step 1 and step 2 and step 3 worked, do step 4.

    Therefore the execution of the steps is inherently based on some condition concerning previous steps. This semantic information is hidden by using the GOTO.


    "Except for truly parallel sequences (multiple parallel flow charts) I understand what you're getting at, but it can get to a point that using state variables or the equivalent to avoid sequential loops is more unreadable than simply coding several sequential loops. Likewise, sometimes recursion is more readable than creating a virtual stack of state variables to accomplish the equivalent."
    True enough, but it remains true that the GOTO is not technically required in any programming language - whether it makes some parallel programming more straightforward or not is another matter entirely.

    I think that's a very dangerous game you're playing with pointers. It seems completely unnecessary to me. I would simply do it using state variables:

    Code (Text):


    void func(void)
    {
       switch(message)
       {
          // cases...
          case doNextStep:
             switch(nextStep)
             {
                // cases...
                case firstState:
                   stateFunction1();
                   break;
                // cases...
             }

             break;
          // cases...
       }
    }

    void stateFunction1(void)
    {
       // do something...
       nextState = secondState;
    }

    // other state functions...

     
    Another option in C is to use a state table. This is closer to what you are doing, but better IMHO. In C++ I think state tables are a great idea, and naturally lend themselves to a class:

    Code (Text):


    class StateTableADT
    {
    public:

       // add an entry to the state table
       addState( stateNumber, stateFunction , nextState );

       // remove an entry from the state table
       removeState( stateNumber );

       // get the current state
       getState();

       // set the current state
       setState();

       // go to the next state and execute
       next();
    };

     
    This is at least a little better than what you're doing with raw function pointers, though, granted, it's not possible in straight C.
     
  5. Apr 21, 2009 #4
    Re: goto

    I'm generally opposed to goto, but Jeff, you raise a point with your status_from_step_1, status_from_step_2, status_from_step_3 nested conditional example. The excess indentation for the nested conditional is unsightly. If you had 15 steps, it might be a legibility problem (although if you have 15 steps, perhaps you're going about it the wrong way).

    One way to solve the problem in structured programming is to have a function which does steps 1,2,3, and instead of goto exit0, simply return from the function.
     
  6. Apr 21, 2009 #5

    rcgldr

    User Avatar
    Homework Helper

    Re: goto

    I was thinking more along the lines of a recipe sequence for cooking. Obvious if step 2 requires butter, and there is no butter, then the recipe stops at step 2 due to failure, yet the steps in a recipe sequence aren't indented based on the outcome of previous steps. Normally all the requirements are dealt with at the start, ingredients and utensils; this might require the equivalent of 10 conditional statements, but all are independent requirements and should not be written to imply some inter-dependency. For example, if the requirement are allocated memory, an opened input file, and an opened output file, I wouldn't want the conditionals to be nested or the main line of code to have 3 levels of indention.

    The condition is sucessful completion of the previous step, this is inherent with any sequential process. A reasonable exception is when a failure simply means to use an alternative method. For example, if there is no ASPI library on a XP system, then the alternative is to use the native API on XP for device operations.

    Depends on the language, some languages only allow one or more branch labels to be specified as one or more of the results of a conditional.

    The problem with this is that adding a step requires updating the message handler as well as updating the step handlers. This creates an uneeded overhead. If there are a large number of steps, or sequences, the switch case statements get huge. In the case of a old driver I rewrote, the interrupt handler had over 200 cases. I converted this to use a small set of pointer to functions, with seperate end and error action handlers, and the concept of nested end and error action handlers, so a high level multi-step sequence could initiate a series of mid level multi-step sequences, and each of those could initate a series of low level multi-step sequences, each sequence with it's own end and error action pointer to function.

    In the case of windows programming in C++ via the predefined classes, the programmer overrides default message handlers by overriding class functions (the equivalent of function pointers), not by adding cases to a switch statement as it's done in C.
     
    Last edited: Apr 21, 2009
  7. Apr 21, 2009 #6

    Hurkyl

    User Avatar
    Staff Emeritus
    Science Advisor
    Gold Member

    Re: goto

    Yuck! I would assert that implementing this literally would be a Bad Thing. Upon failure, this algorithm is supposed to cease continuing -- but this approach has the program continuing through the rest of the algorithm, forcing each individual statement to be guarded against accidental execution after failure.

    There are at least three serious problems:
    1. Each time a new statement is added to the algorithm, you would have to craft a new conditional to guard it against execution after failure.

    2. You have to remember to do so. (And you have to make sure the next person to touch your code is aware of this important fact)

    3. If a new failure mode is introduced by new statements, every subsequent conditional has to be modified to catch the new failure mode.


    The goto version, the nested if-block version, and the function-with-return version all do the Right Thing -- upon failure, flow immediately proceeds directly to where it's supposed to go.



    Of course, my statements aren't absolute. I'm sure there are some algorithms that are most naturally expressed in the way you did. But I assert that such algorithms are few and far in-between, and most algorithms are better expressed in the form

    If any of the following steps fails, skip forward to handle failure:
    * do step 1
    * do step 2
    * do step 3
    * do step 4

    with the specific manner of skipping left up to the implementation. (goto, return, nested if's, exceptions, state machines (via various methods))



    I would like to assert an axiom:
    If it's acceptable in some situation to emulate a goto through other flow control mechanisms, then it's even better to just use a goto.​
     
  8. Apr 21, 2009 #7

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    Re: goto

    From the other thread, to keep in line with Jeff's highjack avoidance:
    False. Boehm and Jacopini's paper says any non-structured program can be built up using sequences, while loops, and alternation. The construction starts by wrapping a while loop around the whole program. The construction then adds sequences, loops, and alternations. There is nothing to stop the construction from nesting a loop within a loop; it is trivial to prove that such nested loops are needed to emulate the non-structured code.

    Besides, the theorem has been falsified. Another construct beside sequence, loop, and alternation is needed. Loops with multi-level breaks work, for example.

    Kozen, Dexter and Tseng, Wei-Lung Dustin, "The Böhm–Jacopini Theorem Is False, Propositionally", Lecture Notes in Computer Science, 5133 177-192, Springer-Verlag, 2008
    http://ecommons.cornell.edu/handle/1813/9478


    Goto-less programming can make for some extremely ugly code at times, particularly in projects where some structured programming fanatic project manager has ruled out break statements and has mandated the "single point of entry / single point of return" rule.

    OTOH, when asked to review some butt ugly code, whether goto-less or not, my first question to the author is always "why did you make this code so ugly?"

    Surely you jest! While strict adherence to structured programming can be considered harmful, this axiom advocates complete abandonment of structured programming guidelines. They are excellent guidelines. The problems arise then these guidelines are made into rigid, no-exception-granted rules.
     
  9. Apr 21, 2009 #8

    Hurkyl

    User Avatar
    Staff Emeritus
    Science Advisor
    Gold Member

    Re: goto

    I'm not trying to advocate using goto when flow is naturally described by other mechanisms -- what I'm trying to advocate is to reject using the other mechanisms to implement flow that is most naturally expressed via goto.

    The flagship example is the break out of double loop:

    Code (Text):

      for(int i = 0; i < 100; ++i) {
        for(int j = 0; j < 100; ++i) {
          if(table[i][j] == 0) { goto end_of_loop; }
        }
      }
    end_of_loop:
     
    (aside: one of the few things I give java high praise for is having multi-level break statements to deal with this)

    Yes, you could have turned this loop into a function that exits via return. If it should be a function, then make it a function. But I reject the idea that "eliminating goto" is just cause for making it a function.

    Worse is this style of "fix":

    Code (Text):

      int flag = 0;
      for(int i = 0; i < 100; ++i) {
        for(int j = 0; j < 100; ++i) {
          if(table[i][j] == 0) { flag = 1; break; }
        }
        if(flag) { break; }
      }
     
    which breaks the jump into smaller pieces, which is considerably more complicated and error-prone, becoming very awkward if the loops are doing more complex work and if there are other skips to be made.

    Standard python style, I believe, implements this as

    Code (Text):

    class end_of_loop_exception:
      pass
    try:
      for i in xrange(100):
        for j in xrange(100):
          if table[i][j] == 0:
            raise end_of_loop_exception
    except end_of_loop_exception:
      pass
    # more code here
     
    I assert this code snippet would be better implemented as goto statement. (Of course, python doesn't have them, so it uses exceptions to emulate them) I still prefer this to the flag variable approach.



    (Since being introduced to python's "for...else" construct, I find myself using that flow control in C++ from time to time -- implemented with goto, though. It has the benefits of requiring neither flag variables nor making duplicate copy of the loop condition, both of which I find undesirable)
     
  10. Apr 21, 2009 #9

    rcgldr

    User Avatar
    Homework Helper

    Re: goto

    I've never understood why many feel that returns scattered throughout a function are acceptable, but not goto's to a common exit point, or as in your case to break out of a nested loop. One disadvantage of the return is the loss of the current context (local variables).

    I agree; it violates one of my rules about making the same decision twice. It creates a second uneeded conditional, and the more conditionals in a program the more paths to check and the more that can go wrong.

    Grateful to find that I'm not the only remaining programmer in the world that is not "anti-goto".
     
  11. Apr 21, 2009 #10

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    Re: goto

    I think we are in violent agreement then. While goto can be considered harmful, so can strict adherence to goto-less programming. I've always viewed handling of exceptional conditions as an acceptable exception to the "no goto" rule. Bottom line: If the use of goto makes sense and the workarounds around it are just plain ugly (e.g., getting rid of the gotos bumps code's extended cyclomatic complexity from 8 to 18), use the goto.
     
  12. Apr 21, 2009 #11
    Re: goto

    "Yuck! I would assert that implementing this literally would be a Bad Thing. Upon failure, this algorithm is supposed to cease continuing -- but this approach has the program continuing through the rest of the algorithm, forcing each individual statement to be guarded against accidental execution after failure."
    I wasn't suggesting you actually code it like that. I was just saying that those are the semantics of nested if-then-else statements. Even though you don't write it all out, it can be implied that those are the preconditions required for execution.

    "If it's acceptable in some situation to emulate a goto through other flow control mechanisms, then it's even better to just use a goto."
    I'm not sure how much sense that makes since, in reality, all control flow constructs emulate the goto.

    "False. Boehm and Jacopini's paper says any non-structured program can be built up using sequences, while loops, and alternation. The construction starts by wrapping a while loop around the whole program. The construction then adds sequences, loops, and alternations. There is nothing to stop the construction from nesting a loop within a loop; it is trivial to prove that such nested loops are needed to emulate the non-structured code. Besides, the theorem has been falsified. Another construct beside sequence, loop, and alternation is needed. Loops with multi-level breaks work, for example."
    False. Their proof showed that all deterministic programs described by flowgraphs can be represented as a single while loop and if-then-else blocks. The only reason to introduce multi-level breaks and nested loops is to eliminate the need for auxiliary variables... or at least that was my understanding. Perhaps you can furnish a counterexample to their proof, and we'll see if I can't use their method to get a program with one outer while loop and if-then-else blocks.

    And Hurkyl, the way I solve that problem of yours is to put the semantic information where it belongs, namely, in the test condition:

    Code (Text):

    bool running = true;
    for(int i = 0; i < 100 && running; i++)
    {
       for(int j = 0; j < 100 && running; j++)
       {
          if (table[i][j] == 0)
          {
             running = false;
          }
          else
          {
             // rest of code...
          }
       }    
    }
     
    The benefit of this is that's it's blindingly obvious whether or not the nested loops actually covered all 100 x 100 cases. In practice, I would probably reverse the order of the if block... do the case where it's not 0 first, and then handle the = 0 with the else. I did it the way I did above for illustrative purposes. Generally, it's better practice to put your normal use scenarios before your exception ones.
     
  13. Apr 21, 2009 #12
    Re: goto

    "I've never understood why many feel that returns scattered throughout a function are acceptable,"
    I know, right? That's even worse than using gotos. For my money, I don't think that returns should be allowed except as the last line of a function.

    "Grateful to find that I'm not the only remaining programmer in the world that is not "anti-goto"."
    I wouldn't say I'm "anti-goto"... I certainly think the only thing to do is to use other constructs when it is reasonable to do so. I can't think of a time when the GOTO is the only reasonable construct, but that doesn't mean there aren't any.

    "I think we are in violent agreement then. While goto can be considered harmful, so can strict adherence to goto-less programming. I've always viewed handling of exceptional conditions as an acceptable exception to the "no goto" rule. Bottom line: If the use of goto makes sense and the workarounds around it are just plain ugly (e.g., getting rid of the gotos bumps code's extended cyclomatic complexity from 8 to 18), use the goto."
    I think that's a well-reasoned opinion. The only difference between that and my opinion is that mine would be reversed, that is, ...

    "I think we are in violent agreement then. While goto can be considered harmful, so can strict adherence to goto-less programming. I've always viewed handling of exceptional conditions as an acceptable exception to the "no goto" rule. Bottom line: If the use of structured programming makes sense and gotos are just plain ugly (e.g., using gotos hides semantic information and substantially decreases readability), use structured progranming."
    We're really saying the same thing, I think, just with different spin. Everybody likes their own brand, you know.
     
  14. Apr 21, 2009 #13

    rcgldr

    User Avatar
    Homework Helper

    Re: goto

    While we're on the subject of avoiding goto's this is one of my pet peaves:

    Code (Text):

        step1;
        if(step1_status == OK)
        {
            step2;
            if(step2_status == OK)
            {
                step3;
                ...
            }
            else
            {
                handle step 2 failure
            }
        }
        else
        {
            handle step 1 failure
        }
     
    This is because the error handling code for step 1 ends up the furthest away from step 1, with the error handling for step 2 the nest furthest away from the step2, ... I prefer this:

    Code (Text):

        step1;
        if(step1_status != OK)
        {
            handle step1 error;
            goto exit0;
        }
        step2;
        if(step2_status != OK)
        {
            handle step2 error;
            goto exit0;
        }
        step3;
    exit0:
     
    or this equivalent to throw+catch, since at least I have a label I can search for rather than trying to find the 2nd level of indention trailing brace and else statement for step2_status == OK. Also this separates the error handling code from the main sequence of code where errors are not expected.

    Code (Text):

        step1;
        if(step1_status != OK)
            goto step1error:
        step2;
        if(step2_status != OK)
            goto step2error;
        step3;
        goto exit0:

    step1error:
        handle step1 error;
        goto exit0;

    step2error:
        handle step2 error;
        goto exit0;

    exit0:
        return(...)
     
     
    Last edited: Apr 21, 2009
  15. Apr 21, 2009 #14

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    Re: goto

    AUMathTutorial,
    1. Please, please, please learn how to use the quote feature.
    2. You appear to be in the camp of rabid structured programming fanatics. We are not really saying the same thing.
     
  16. Apr 21, 2009 #15

    Hurkyl

    User Avatar
    Staff Emeritus
    Science Advisor
    Gold Member

    Re: goto

    One drawback is that the implementation of one idea (break out of the loop if we find a zero) has now been spread over five lines of code (not counting the else and the extra {}), which could be widely separated depending upon what else is in the loop. (especially if you reverse the order of the if block)

    A more minor drawback that's only relevant for code that needs to be fast is that you've made the loop condition more complicated, which may make it more difficult for the compiler to optimize. (e.g. I expect the compilers on a cray vector machine to have a significantly easier time dealing with the early exit than with the flag variable / more complicated loop condition)

    The more serious drawback is it's an error-prone flag variable solution. The obvious trap you've laid is:

    Code (Text):

    bool running = true;
    for(int i = 0; i < 100 && running; i++)
    {
       for(int j = 0; j < 100 && running; j++)
       {
          if (table[i][j] == 0)
          {
             running = false;
          }
          else
          {
             // rest of code...
          }
       }    
       cout << "Didn't find a zero in row " << i << endl;
    }
     
     
  17. Apr 21, 2009 #16

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    Re: goto

    All I can say to this rule is YUCK. All caps yuck, and I probably should have used bold and put it in a humongous font to boot. The "single point of entry / single point of return" rule is responsible for some incredibly hideous code.

    I can see arguments for both ways of implementing things (handle exceptions first versus last), and I use both schemes. If the exception handling is exceptionally short (e.g., print an error message and call exit()), I tend to deal with the exception first to get it out of the way. If the major point of the function is to ensure that exceptional conditions are address, I try to deal with exceptional cases as soon as they arise. If, on the other hand, the exception handling is peripheral and gets in the way of understanding the main purpose of the code, the exception handling comes last.

    I tend to be a defensive programmer and add tests for conditions I know will never occur. Amazingly, some other programmer or user inevitably finds a way to invoke those "this-can't-happen-but-just-in-case" tests (at which point I get a phone call or e-mail because I wrote an insipid error message along the lines of "This can't happen. FIXME").
     
  18. Apr 21, 2009 #17

    rcgldr

    User Avatar
    Homework Helper

    Re: goto

    Regarding the pointer to function usage, an example snippet from a I/O based program written in C (this could be implemented by overiding member functions in C++):

    Code (Text):

        status = loadlibrary(ASPI);
        if(status == OK){
            pfRead = ReadAspi;
            pfWrite = WriteAspi;
        }
        else
        {
            pfRead = ReadNative;
            pfWrite = WriteNative;
        }

        ...
        *(pfRead)(...);
        ...
        *(pfWrite)(...);
     
    This usage of pointer to function eliminates having conditionals on every I/O statement in the program. Plus if a third set of I/O function became available, only the initialization code has to be changed.
     
  19. Apr 21, 2009 #18

    rcgldr

    User Avatar
    Homework Helper

    Re: goto

    True, but for handling exceptions last, I prefer using gotos and human readable labels, instead of searching through 4 levels of nested if else statements (which I've seen).

    In my apparently heavy usage pointer to functions, I always initialize them to a "this shouldn't happen" function, and they've been triggered a few times.
     
  20. Apr 21, 2009 #19

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    Re: goto

    What's all this stuff about searching for those close braces? What kind of primitive editor are you using?
     
  21. Apr 21, 2009 #20

    rcgldr

    User Avatar
    Homework Helper

    Re: goto

    Some of my jobs involve some relatively antiquated editors and toolsets, especially embedded firmware on some arcane CPU. Plus I've seen some really bad examples of nested if else statments that spanned several screens of code. Split screen editors that let you look at two or more places in the same file via multiple windows help out here. In the case of limited capablity debuggers, sometimes it's better to write the code closer to how it will be implemented in assembly code, so when working with the debugger, the correspondence between source and binary code is close.
     
    Last edited: Apr 21, 2009
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook




Similar Discussions: Search for a label than a trailing right brace
Loading...