• (cs) in reply to dubwai
    dubwai:

    I don't even use labelled breaks.  Never wanted to.



    Labeled breaks/loop control statements can be quite nice in Perl.  I think they're even required to be labeled.

    But then again, Perl seems to be a bastard language to some on this forum. [:)]
  • (cs) in reply to mizhi
    mizhi:
    Labeled breaks/loop control statements can be quite nice in Perl.  I think they're even required to be labeled.

    They don't have to be labeled.

  • (cs) in reply to mizhi
    mizhi:
    dubwai:

    I don't even use labelled breaks.  Never wanted to.



    Labeled breaks/loop control statements can be quite nice in Perl.  I think they're even required to be labeled.

    But then again, Perl seems to be a bastard language to some on this forum. [:)]

    I've had to maintain so horribly written code, I can't even imagine using perl on multi-person projects.  It's just too wide open.

    I guess that's why I like Java.  I don't have to lookup syntax.  There's only a one way to write most things in terms of the syntax.  I work on code that has dozens of poeple in it, a lot of whom I've never met and often don't know their names (current developers, not just legacy) so the more we can limit the trouble they can get into, the better.

  • anonymous (unregistered) in reply to dubwai

    Of course Java has go-to's, they're called "Label statements" but they're go-to's just the same.

    I find the programmer who yells "you can't use go-to's!" the loudest usually can't explain why.. it's quite reminicent of that management-monkey comparison involving a ladder and bananas.

  • (cs) in reply to Maurits

    Totally backwards.

    for(bool doCheck = true; !doCheck || sysmgr->getProcessCount !=0; doCheck=true)
    {
        if(!sysmgr->hasPorkRinds)
        {
             doCheck = false;
             continue;
        }
    }

    yes, continue resets doCheck back to true, however, this is a desirable effect, as continue thus will behave normally if called subsequently in a situation where conventional continue behavior is wanted.

    With your setup, using continue would be effectively equivalent to using break, as setting docheck to false would cause the test to fail completely and terminate the loop.

  • (cs) in reply to sadmac

    Last post was in regard to Mauritis' solution. Damn this board software!

  • (cs) in reply to anonymous

    Anonymous:
    Of course Java has go-to's, they're called "Label statements" but they're go-to's just the same. I find the programmer who yells "you can't use go-to's!" the loudest usually can't explain why.. it's quite reminicent of that management-monkey comparison involving a ladder and bananas.

    No, they are no gotos.  They, like if, switch, throw, while etc.do one thing that you can do with gotos but they aren't gotos.

    In any event, like I said, I don't like them and haven't once used them in five years of programming in Java.  To this date, no one has shown me a good example of when they are better or necessary.  So until you can put your money where your mouth is, anonymous coward, I'm not impressed.

  • (cs) in reply to dubwai
    dubwai:

    I've had to maintain so horribly written code, I can't even imagine using perl on multi-person projects.  It's just too wide open.


    Yeah, to be honest, I've never seen any really large projects done in Perl.  MovablType is an exception, but I don't think they have a large staff.


  • bit (unregistered) in reply to dubwai
    dubwai:

    So until someone can show me a code snippet that uses gotos and cannot be rewritten not to use them, I have to believe that gotos are never necessary.  I've not used a single one since I learned to program in basic.  I've never even desired to use them.  I don't even use labelled breaks.  Never wanted to.

    Oh, but anything can be rewritten in assembly, so every single high level construct (and thus every single programming language) is unnecessary.

  • (cs) in reply to sadmac
    sadmac:
    yes, continue resets doCheck back to true, however, this is a desirable effect, as continue thus will behave normally if called subsequently in a situation where conventional continue behavior is wanted.


    doCheck && ... vs. !doCheck || ... yes, you're right.  Silly me.

    "continue" reseting doCheck back to true - no, you're wrong.  If you do a continue, the third thing in the for loop is executed BEFORE evaluating the second thing in the for loop.  So the third chunk of the for loop should be a no-op, and the doCheck = true should appear at the end.
  • (cs) in reply to dubwai
    dubwai:

    The thing about the 'gotos are sometimes necessary' argument is that Java doesn't allow gotos yet I've never heard of a single one of the thousands of Java projects failing for the want of a goto.

    So until someone can show me a code snippet that uses gotos and cannot be rewritten not to use them, I have to believe that gotos are never necessary.  I've not used a single one since I learned to program in basic.  I've never even desired to use them.  I don't even use labelled breaks.  Never wanted to.



    The #1 good use of goto is to implement a very simple and low-overhead version of exception handling. The following C code could be rewritten without goto, but it would be much longer and uglier.

    Without the gotos, you would have to repeat the call to releaseBacon() 5 times, releaseLettuce() 4 times, etc. Futhermore, when some later programmer comes along and decides that obviously bread should be allocated first, (s)he only has to reorder the error handleing in one place - thus reducing the odds of him/her getting it wrong and creating a fixins leak.

    /* what a silly example */
    struct sammich *get_sammich(int *err) {
        struct bacon *thebacon;
        struct lettuce *thelettuce;
        struct tomato *thetomato;
        struct bread *thebread;
        struct sammich *thesammich;

        thebacon = getBacon();
        if (thebacon== NULL)
           goto out1;

        thelettuce =  getLettuce();
        if (thelettuce == NULL)
           goto out2;

        thetomato = getTomoto();
        if (thetomato== NULL)
           goto out3;

        thebread = getBread();
        if (thebread == NULL)
           goto out4;

        /* all resource allocations successful! */
        thesammich = make_sammich(thebacon, thelettuce, thetomato, thebread);
        if (thesammich == NULL)
           goto out5;

        /* yummy */
        return thesammich;

    out5:
        cleanupSammich();
    out4:
        releaseBread();
    out3:
        releaseTomato();
    out2:
        releaseLettuce();
    out1:
        releaseBacon();
        syslog(LOG_EMERG, "%s\n", "get_sammich failed!!!");
        return NULL;
    }
  • (cs) in reply to thalience

    GOTO Hell.

  • (cs) in reply to thalience
    thalience:

    The #1 good use of goto is to implement a very simple and low-overhead version of exception handling. The following C code could be rewritten without goto, but it would be much longer and uglier.


    Preview should let you see if if 3 other people said the same thing (better) before you got done writting a post. :P

    But hey, at least mine was whimsical. :D
  • (cs) in reply to Ytram

    Ytram:
    Trev:

    There are cases where goto is very useful, and actually helps readability.  Non-throwing procedure calls can benefit:

    void MakeDeviceObject()
    {
        int iErr;
        iErr = GetDevice();
        if( iErr == ERROR ) goto l_Err;

        iErr = SetDeviceParameter( "Goblin" );
        if( iErr == ERROR ) goto l_Err;

        ...

        return;

        l_Err:
        DestroyDevice();
        Log( "Error making device object!" );
    }



    How is that any better than this:

    void MakeDeviceObject()
    {
        try
        {
            GetDevice();
            SetDeviceParameter( "Goblin" );
             ...
        }
        catch(DeviceException)
        {
            DestroyDevice();
            Log( "Error making device object!" );

        }
    }


    ermmm... he said "Non-Throwing Procedure calls". Your code won't do much good when DeviceException doesn't get thrown at all. And not every language even has exceptions

  • (cs) in reply to dubwai
    dubwai:

    Anonymous:
    Of course Java has go-to's, they're called "Label statements" but they're go-to's just the same. I find the programmer who yells "you can't use go-to's!" the loudest usually can't explain why.. it's quite reminicent of that management-monkey comparison involving a ladder and bananas.

    No, they are no gotos.  They, like if, switch, throw, while etc.do one thing that you can do with gotos but they aren't gotos.

    In any event, like I said, I don't like them and haven't once used them in five years of programming in Java.  To this date, no one has shown me a good example of when they are better or necessary.  So until you can put your money where your mouth is, anonymous coward, I'm not impressed.

    Actually Java does have gotos, as does the JVM. It just wont allow you to compile it.

    Run a class binary through a de-compiler and enjoy the gotos in the resulting code.

     

  • (cs) in reply to Quinnum
    Quinnum:
    Actually Java does have gotos, as does the JVM. It just wont allow you to compile it.

    Run a class binary through a de-compiler and enjoy the gotos in the resulting code.


    You're confusing Java code with Java bytecode.  Java bytecodes have gotos.  Java code does not.

  • (cs) in reply to thalience
    thalience:

    The #1 good use of goto is to implement a very simple and low-overhead version of exception handling. The following C code could be rewritten without goto, but it would be much longer and uglier.

    Without the gotos, you would have to repeat the call to releaseBacon() 5 times, releaseLettuce() 4 times, etc. Futhermore, when some later programmer comes along and decides that obviously bread should be allocated first, (s)he only has to reorder the error handleing in one place - thus reducing the odds of him/her getting it wrong and creating a fixins leak.

    /* what a silly example */
    struct sammich *get_sammich(int *err) {
        struct bacon *thebacon;
        struct lettuce *thelettuce;
        struct tomato *thetomato;
        struct bread *thebread;
        struct sammich *thesammich;

        thebacon = getBacon();
        if (thebacon== NULL)
           goto out1;

        thelettuce =  getLettuce();
        if (thelettuce == NULL)
           goto out2;

        thetomato = getTomoto();
        if (thetomato== NULL)
           goto out3;

        thebread = getBread();
        if (thebread == NULL)
           goto out4;

        /* all resource allocations successful! */
        thesammich = make_sammich(thebacon, thelettuce, thetomato, thebread);
        if (thesammich == NULL)
           goto out5;

        /* yummy */
        return thesammich;

    out5:
        cleanupSammich();
    out4:
        releaseBread();
    out3:
        releaseTomato();
    out2:
        releaseLettuce();
    out1:
        releaseBacon();
        syslog(LOG_EMERG, "%s\n", "get_sammich failed!!!");
        return NULL;
    }


    I'd be inclined to write this without goto, and break the usual indentation rules instead:

        thebacon = getBacon();
        if (thebacon!= NULL) {

        thelettuce =  getLettuce();
        if (thelettuce != NULL) {

    /* snip */

        }
        releaseLettuce();
        }
        releaseBacon();

    which also allows tear-down to be re-ordered without having to change indentation.

    If you can afford the overhead, then you should consider making the releaseFoo() functions NULL-safe and calling them all on failure, so that the order of tear-down need not be kept in sync with the order of build-up.

  • Trev (unregistered) in reply to emurphy
    emurphy:

    I'd be inclined to write this without goto, and break the usual indentation rules instead:

        thebacon = getBacon();
        if (thebacon!= NULL) {

        thelettuce =  getLettuce();
        if (thelettuce != NULL) {

    /* snip */

        }
        releaseLettuce();
        }
        releaseBacon();

    which also allows tear-down to be re-ordered without having to change indentation.

    If you can afford the overhead, then you should consider making the releaseFoo() functions NULL-safe and calling them all on failure, so that the order of tear-down need not be kept in sync with the order of build-up.


    IMO it's no more readable, and less flexible.  *shrug*  Just a style at that point, but breaking language structure (even if just a little) just to avoid something valid for no real reason seems more obfuscating.  Yes, it's easy to abuse goto.  But using goto isn't necessarily abuse.
  • Tom (unregistered)

    Yep, the old "goto".  The refuge of coders who don't know how to plan a function or method.  Nothing like seeing coding styles I first saw in the 1970's....

  • (cs) in reply to Tom

    Well, have pity on us java guys, we have to make do with labels and named break/continue...

  • (cs) in reply to Trev
    Anonymous:
    Alex Papadimoulis:
    Everybody knows that you should never use "goto" statements. Well, except in one or two rare circumstances that you won't come across anyway. But even when you do come across those situations, they're usually "mirage cases" where there's no need to "goto" anyway.


    There are cases where goto is very useful, and actually helps readability.  Non-throwing procedure calls can benefit:


    void MakeDeviceObject()

    {

        int iErr;

        iErr = GetDevice();

        if( iErr == ERROR ) goto l_Err;


        iErr = SetDeviceParameter( "Goblin" );

        if( iErr == ERROR ) goto l_Err;


        ...


        return;


        l_Err:

        DestroyDevice();

        Log( "Error making device object!" );

    }




    Why don't  you just use try catch constructs? 

  • (cs) in reply to Jon Limjap

    Jon Limjap:
    Anonymous:
    Alex Papadimoulis:
    Everybody knows that you should never use "goto" statements. Well, except in one or two rare circumstances that you won't come across anyway. But even when you do come across those situations, they're usually "mirage cases" where there's no need to "goto" anyway.


    There are cases where goto is very useful, and actually helps readability.  Non-throwing procedure calls can benefit:

    void MakeDeviceObject()
    {
        int iErr;
        iErr = GetDevice();
        if( iErr == ERROR ) goto l_Err;

        iErr = SetDeviceParameter( "Goblin" );
        if( iErr == ERROR ) goto l_Err;

        ...

        return;

        l_Err:
        DestroyDevice();
        Log( "Error making device object!" );
    }



    Why don't  you just use try catch constructs? 

    Or have a new method?

  • (cs) in reply to anonymous

    Anonymous:
    I find the programmer who yells "you can't use go-to's!" the loudest usually can't explain why.. it's quite reminicent of that management-monkey comparison involving a ladder and bananas.

    Well, the "technical" reason why GOTOs are "bad" is because they make formal verification of program logic difficult.  I don't know if they even teach formal verification classes any more, but that's where I saw the purely logic-based explanation of why they should be avoided.  Formal verification isn't used in most places, but I find that when you are relying on GOTOs, it's usually because you don't have a clear idea of what you are doing or you haven't really organized your code properly.

  • (cs) in reply to mizhi
    mizhi:
    dubwai:

    I've had to maintain so horribly written code, I can't even imagine using perl on multi-person projects.  It's just too wide open.


    Yeah, to be honest, I've never seen any really large projects done in Perl.  MovablType is an exception, but I don't think they have a large staff.


    Livejournal is also primarily written in perl, plus C and their own bizarro BML as well. And that is a huge codebase. (That's probably why they hooked up with sixapart, Perl geeks belong together.)

    thalience seems to get it; try/catch/finally are thinly veiled structured gotos, which give you the power to implement them in normal C code, and can actually make code cleaner than cleanup in a catch/finally block, but only if implemented correctly and consistently. (Macro time.)

    The original code is just a really bad use of gotos by someone too new or drunk to think through the proper sequence of booleans to arrive at his result.


    phelyan:
    Jon Limjap:
    Anonymous:
    Alex Papadimoulis:
    Everybody knows that you should never use "goto" statements. Well, except in one or two rare circumstances that you won't come across anyway. But even when you do come across those situations, they're usually "mirage cases" where there's no need to "goto" anyway.


    There are cases where goto is very useful, and actually helps readability.  Non-throwing procedure calls can benefit:

    void MakeDeviceObject()
    {
        int iErr;
        iErr = GetDevice();
        if( iErr == ERROR ) goto l_Err;

        iErr = SetDeviceParameter( "Goblin" );
        if( iErr == ERROR ) goto l_Err;

        ...

        return;

        l_Err:
        DestroyDevice();
        Log( "Error making device object!" );
    }



    Why don't  you just use try catch constructs? 

    Or have a new method?

    It's standard (lazy) VB error handling in C. The obvious collary is that it's possible to have C/C++-style error handling in VB, with some effort. I've seen examples of such online.

  • Zahlman (unregistered) in reply to Aristotle Pagaltzis
    Aristotle Pagaltzis:

    Well, I certainly prefer this:

    int foo() {
    void * res1, * res2, * res3, * res4, * res5;
    int result = -1;

    res1 = alloc_res1();
    if( ! res1 ) goto leave;

    res2 = alloc_res2( res1 );
    if( ! res2 ) goto dealloc_res1;

    res3 = alloc_res2( res2 );
    if( ! res3 ) goto dealloc_res2;

    res4 = alloc_res2( res3 );
    if( ! res4 ) goto dealloc_res3;

    res5 = alloc_res2( res4 );
    if( ! res5 ) goto dealloc_res4;

    do_something( res5 );

    result = 0

    dealloc_res( res5 );

    delloc_res4:
    dealloc_res( res4 );

    delloc_res3:
    dealloc_res( res3 );

    delloc_res2:
    dealloc_res( res2 );

    delloc_res1:
    dealloc_res( res1 );

    leave:
    return result;
    }

    over this:

    int foo() {
    void * res1;
    int result = -1;

    res1 = alloc_res1();
    if( res1 ) {
    void * res2 = alloc_res2( res1 );

    if( res2 ) {
    void * res3 = alloc_res2( res2 );

    if( res3 ) {
    res4 = alloc_res2( res3 );

    if( res4 ) {
    res5 = alloc_res2( res4 );

    if( res5 ) {
    do_something( res5 );
    result = 0;
    dealloc_res( res5 );
    }

    dealloc_res( res4 );
    }

    dealloc_res( res3 );
    }

    dealloc_res( res2 );
    }

    dealloc_res( res1 );
    }

    return result;
    }


    Bah.

    int foo() {
    void *res1 = *res2 = *res3 = *res4 = *res5 = NULL;
    int result = -1;

    /* Conditionally allocate stuff on previous allocation.
    As soon as anything fails, the rest will fail */
    res1 = alloc_res1();
    res2 = res1 && alloc_res2( res1 );
    res3 = res2 && alloc_res2( res2 );
    res4 = res3 && alloc_res2( res3 );
    res5 = res4 && alloc_res2( res4 );

    if (res5) {
    do_something( res5 );
    result = 0;
    dealloc_res( res5 );
    }

    /* Deallocate everything which actually got allocated. */
    if (res4) dealloc_res( res4 );
    if (res3) dealloc_res( res3 );
    if (res2) dealloc_res( res2 );
    if (res1) dealloc_res( res1 );
    return result;
    }

    Although in C++ you would probably solve the problem with RAII. And personally, I've never run into a need for anything like this (which is always the common example) "in all my years".

  • (cs) in reply to JohnO

    JohnO:
      Formal verification isn't used in most places, but I find that when you are relying on GOTOs, it's usually because you don't have a clear idea of what you are doing or you haven't really organized your code properly.

    Actually, it's used quite a bit --- by the optimizer built into every modern compiler.  The better the compiler can track the usage of variables, the better it can optimize the code.  Loop structures make it easy to track variable lifetimes.  Gotos confuse the analysis.

  • Z (unregistered) in reply to dubwai
    dubwai:

    The thing about the 'gotos are sometimes necessary' argument is that Java doesn't allow gotos yet I've never heard of a single one of the thousands of Java projects failing for the want of a goto.



    Well, in Java programs people usually don't care about performance or well-structured control flow. (note: I think some uses of goto are good because they make the program easier to understand and reason about, and clearer programs are IMHO a Good Thing(tm))

    dubwai:

    So until someone can show me a code snippet that uses gotos and cannot be rewritten not to use them, I have to believe that gotos are never necessary.  I've not used a single one since I learned to program in basic.  I've never even desired to use them.  I don't even use labelled breaks.  Never wanted to.



    For an enlightening discussion about possible valid uses of goto-statements, I would recommend the following article:

    			<table border="0" cellpadding="0" cellspacing="0">
    			<col width="10%">
    			<col width="1%">
    			<col width="89%">
    			<tbody><tr>
    				<td colspan="3">
    					<a href="http://portal.acm.org/citation.cfm?id=356640&amp;coll=ACM&amp;dl=ACM&amp;CFID=49877412&amp;CFTOKEN=76569091" class="medium-text" target="_self">Structured Programming with <italic>go to</italic> Statements</a>
    						
    	 					<div class="authors">
    							
    								Donald E. Knuth
    							
    	 					</div>
    				</td>
    			</tr>
    			<tr valign="top">
    				<td class="small-text" nowrap="nowrap">
    					
    						December 1974
    					
    				</td>
    				<td> </td>
    				<td>	
    				<div class="addinfo">
    				
    					
    					  	<strong>ACM Computing Surveys (CSUR)</strong><font size="-2">, 
    							
    									Volume 6
    								
    									Issue 4
    								
    							  
    						</font>
    					
    					
    				</div></td></tr></tbody></table></td></tr></tbody>
    

    It gives an overview of the common uses of goto, and the equivalent goto-less programs. One observation that is made (shown in other papers) is that one has to do more computation of use more variables if goto's are to be avoided.
  • Zahlman (unregistered) in reply to thalience
    thalience:
    dubwai:

    The thing about the 'gotos are sometimes necessary' argument is that Java doesn't allow gotos yet I've never heard of a single one of the thousands of Java projects failing for the want of a goto.

    So until someone can show me a code snippet that uses gotos and cannot be rewritten not to use them, I have to believe that gotos are never necessary.  I've not used a single one since I learned to program in basic.  I've never even desired to use them.  I don't even use labelled breaks.  Never wanted to.



    The #1 good use of goto is to implement a very simple and low-overhead version of exception handling. The following C code could be rewritten without goto, but it would be much longer and uglier.

    Without the gotos, you would have to repeat the call to releaseBacon() 5 times, releaseLettuce() 4 times, etc. Futhermore, when some later programmer comes along and decides that obviously bread should be allocated first, (s)he only has to reorder the error handleing in one place - thus reducing the odds of him/her getting it wrong and creating a fixins leak.

    /* what a silly example */
    struct sammich *get_sammich(int *err) {
        struct bacon *thebacon;
        struct lettuce *thelettuce;
        struct tomato *thetomato;
        struct bread *thebread;
        struct sammich *thesammich;

        thebacon = getBacon();
        if (thebacon== NULL)
           goto out1;

        thelettuce =  getLettuce();
        if (thelettuce == NULL)
           goto out2;

        thetomato = getTomoto();
        if (thetomato== NULL)
           goto out3;

        thebread = getBread();
        if (thebread == NULL)
           goto out4;

        /* all resource allocations successful! */
        thesammich = make_sammich(thebacon, thelettuce, thetomato, thebread);
        if (thesammich == NULL)
           goto out5;

        /* yummy */
        return thesammich;

    out5:
        cleanupSammich();
    out4:
        releaseBread();
    out3:
        releaseTomato();
    out2:
        releaseLettuce();
    out1:
        releaseBacon();
        syslog(LOG_EMERG, "%s\n", "get_sammich failed!!!");
        return NULL;
    }


    Bah again. In the case where the resources don't have to be cleaned up on success (as here), it's even easier:

    struct sammich *get_sammich(int *err) {
        struct bacon *thebacon = getBacon();
        struct lettuce *thelettuce = thebacon && getLettuce();
        struct tomato *thetomato = thelettuce && getTomato();
        struct bread *thebread = thebread && getBread();

        if (thebacon && thelettuce && thetomato && thebread) {
            /* OK, we can try to make a sammich. */
            struct sammich *thesammich = make_sammich(thebacon, thelettuce, thetomato, thebread);
            if (thesammich) return thesammich; /* yummy */
        }
        /* We definitely don't ever have to clean up thesammich on failure, because
        it is null :s */

        if (thebread) releaseBread();
        if (thetomato) releaseTomato();
        if (thelettuce) releaseLettuce();
        if (thebacon) releaseBacon();
        syslog(LOG_EMERG, "%s\n", "get_sammich failed!!!");
        return NULL;
    }

    But really, who uses C these days anyway? And even then, I say the release functions are at fault for not being able to handle (i.e. ignore) a null pointer input.
  • (cs) in reply to Zahlman
    Anonymous:


        /* Conditionally allocate stuff on previous allocation.
    As soon as anything fails, the rest will fail */
    res1 = alloc_res1();
    res2 = res1 && alloc_res2( res1 );
    res3 = res2 && alloc_res2( res2 );
    res4 = res3 && alloc_res2( res3 );
    res5 = res4 && alloc_res2( res4 );


    I thought (true_thing && another_true_thing) returned Boolean true,
    rather than another_true_thing. Even if it does return
    another_true_thing, the following would be clearer:

    if (res1) res2 = alloc_res2( res1 );
    if (res2) res3 = alloc_res2( res2 );
    if (res3) res4 = alloc_res2( res3 );
    if (res4) res5 = alloc_res2( res4 );

    Actually, it should probably be

    if (res1) res2 = alloc_res2( res1 );
    if (res2) res3 = alloc_res3( res2 );
    if (res3) res4 = alloc_res4( res3 );
    if (res4) res5 = alloc_res5( res4 );

    (I'm guessing that Aristotle intended to use those functions,
    but copy+pasted and missed making the appropriate changes.)

  • BogusDude (unregistered) in reply to Aristotle Pagaltzis
    Aristotle Pagaltzis:
    dubwai:
    Every example I've seen to this point have been straw man examples. Examples of how gotos make bad code marginally better. They are too long or do too much or should be replaced with a different approach entirely orientation.. I could be wrong, but gotos seem to come from habit or a lack of imagination i.e. being locked into a procedural mindset.

    Well, I certainly prefer this:

    int foo() {
        void * res1, * res2, * res3, * res4, * res5;
        int result = -1;
    
        res1 = alloc_res1();
        if( ! res1 ) goto leave;
    
        res2 = alloc_res2( res1 );
        if( ! res2 ) goto dealloc_res1;
    
        res3 = alloc_res2( res2 );
        if( ! res3 ) goto dealloc_res2;
    
        res4 = alloc_res2( res3 );
        if( ! res4 ) goto dealloc_res3;
    
        res5 = alloc_res2( res4 );
        if( ! res5 ) goto dealloc_res4;
    
        do_something( res5 );
    
        result = 0
    
        dealloc_res( res5 );
    
        delloc_res4:
        dealloc_res( res4 );
    
        delloc_res3:
        dealloc_res( res3 );
    
        delloc_res2:
        dealloc_res( res2 );
    
        delloc_res1:
        dealloc_res( res1 );
    
        leave:
        return result;
    }
    

    over this:

    int foo() {
        void * res1;
        int result = -1;
    
        res1 = alloc_res1();
        if( res1 ) {
            void * res2 = alloc_res2( res1 );
    
            if( res2 ) { 
                void * res3 = alloc_res2( res2 );
    
                if( res3 ) {
                    res4 = alloc_res2( res3 );
    
                    if( res4 ) {
                        res5 = alloc_res2( res4 );
    
                        if( res5 ) {
                            do_something( res5 );
                            result = 0;
                            dealloc_res( res5 );
                        }
    
                        dealloc_res( res4 );
                    }
    
                    dealloc_res( res3 );
                }
    
                dealloc_res( res2 );
            }
    
            dealloc_res( res1 );
        }
    
        return result;
    }
    

    Procedural mindset? What else would you use in C? Sure, I’ve never needed a GOTO in Perl either.

    What the parent means, is that (in a OO language) you should have wrapped res1, etc. inside objects. That doesn't mean you have to wrap every function call, but at least the allocation/deallocation. That way they will get cleaned up properly without the goto's. There's always a way to do things more elegantly without goto's. Don't try to make excuses for using them.

  • (cs) in reply to loneprogrammer

    goto ("FOO", "BAR", "GLARCH")[$i]

    No, that's fine. Well, not fine, but I can claim to have seen worse:

    One night I was called out to fix a batch process that was failing. PL/I and CICS if it matters. Now, this was a system that was "inherited" and we weren't sure if we had up-to-date source for all of it. And, as luck would have it, the part that was breaking didn't.

    Anyway. This particular piece of code had dynamic jumps implemented in PL/I, basically done through arrays of variables whose contents were pointers to routines. 2 levels of indirection, as it was bouncing stuff through 2 jump tables. And the contents of the arrays themselves changed dyamically. Oh yes. Completely fucking impossible to debug.

    Imagine. 3 AM. Out of date source code. What code I had indicated massive WTFery. So I did what any sane person would do. hacked out the troublesome line in the input stream, kicked the process back off, and left it until daylight.

    The problem turned out to not be a problem with the jump table stuff but instead a bounds overflow, the data we were failing on indicated a number of cocktail sticks ordered : by unit[1]. the resulting number was too big for the field in which it was to be contained - BLAM!

    Simon

    [1] Which seems to indicate that a certain chain of off-licenses in the UK can (or could) order single cocktail sticks from their suppliers

  • (cs) in reply to tufty

    LABEL VARIABLE, that's what they were called in PL/I.

    Simon

  • (cs) in reply to tufty

    Those two 'valid' big goto examples are preferrable over a big list of nested ifs, but I believe a switch would be the better solution.

    Somehow I feel the presence of goto in a high-level language defeats the purpose of features like while and for. If we start using goto in scripts, we might as well throw away all languages and train programmers to write machine code directly. Death to while!

    //start loop
    loopstart:
    code;
    code;
    code;
    if (condition) goto loopstart;


  • -L (unregistered) in reply to Ytram
    Ytram:


    How is that any better than this:

    void MakeDeviceObject()
    {
        try
        {
            GetDevice();
            SetDeviceParameter( "Goblin" );
             ...
        }
        catch(DeviceException)
        {
            DestroyDevice();
            Log( "Error making device object!" );

        }
    }




    Cool. But quite bad. When your SetDeviceParameter() raises BeerTooWarmException, you'll have a resource leak, which is not quite obvious.

    But that's not a problem, for every programmer I know can immediately list all the exceptions any given operation raises in modern languages.
  • (cs) in reply to -L

    Anonymous:
    Ytram:


    How is that any better than this:

    void MakeDeviceObject()
    {
        try
        {
            GetDevice();
            SetDeviceParameter( "Goblin" );
             ...
        }
        catch(DeviceException)
        {
            DestroyDevice();
            Log( "Error making device object!" );

        }
    }




    Cool. But quite bad. When your SetDeviceParameter() raises BeerTooWarmException, you'll have a resource leak, which is not quite obvious.

    But that's not a problem, for every programmer I know can immediately list all the exceptions any given operation raises in modern languages.

    Arrgh... and methods like that should not be return type void. Return the device object or throw a checked exception, but don't return null.

  • Vanders (unregistered) in reply to Zahlman

    But really, who uses C these days anyway?

    Alsmot every Operating System and OS-level interface in existence?  Hundreds of millions of lines of code across hundreds of systems.

  • (cs) in reply to dubwai

    dubwai:
    The thing about the 'gotos are sometimes necessary' argument is that Java doesn't allow gotos yet I've never heard of a single one of the thousands of Java projects failing for the want of a goto.

    And yet, no-one has written a high-performance OS in Java...!  [;)]

    Seriously, <FONT face="Courier New" size=2>goto</FONT>s are sometimes used in high-performance/RTOS kind of code, like kernels and drivers.  When dealing with an environment where each instruction counts (like small embedded systems), your outlook changes than when doing average desktop or server development.  Moreso when your outlook on enhancing performance is to just throw another blade in the server!

    ASM has been using <FONT face="Courier New" size=2>goto</FONT> (in various forms) for years, and still does...  They are not evil by themselves; like most things, it is the user that makes it evil.

  • P-Nuts (unregistered) in reply to jtwine

    The original "Goto Considered Harmful" article: http://www.acm.org/classics/oct95/

    However, one legitimate (if infrequent) use of goto is to get out of more than one nested loop, as break/continue only get you out of the first one:

    for (int i=0; i<N; ++i) {
    for (int j=0; j<N; ++j) {
    // do stuff that might not need all the i,j combinations
    if (done) goto finished;
    }
    }
    finished:
    <n ;="" ++i="" {="" for="" (int="" j="0;"><n ;="" ++j="" {="" do="" stuff="" that="" might="" not="" need="" all="" the="" i,j="" combinations="" if="" (done="" goto="" }="" finished=""></n></n>
    The goto-less solution to this problem needs an extra variable, and is arguably less readable:
    bool finished = false;
    for (int i=0; i<N; ++i) {
    for (int j=0; j<N; ++j) {
    // do stuff that might not need all the i,j combinations
    if (done) finished = true;
    }
    if (finished) break;
    }<n ;="" ++i="" {="" for="" (int="" j="0;"><n ;="" ++j="" {="" do="" stuff="" that="" might="" not="" need="" all="" the="" i,j="" combinations="" (done="" if="" (finished="" break="" }="" finished="true;"></n></n>
  • Evan M. (unregistered) in reply to JohnO
    JohnO:

    Anonymous:
    I find the programmer who yells "you can't use go-to's!" the loudest usually can't explain why.. it's quite reminicent of that management-monkey comparison involving a ladder and bananas.

    Well, the "technical" reason why GOTOs are "bad" is because they make formal verification of program logic difficult.  I don't know if they even teach formal verification classes any more, but that's where I saw the purely logic-based explanation of why they should be avoided.  Formal verification isn't used in most places, but I find that when you are relying on GOTOs, it's usually because you don't have a clear idea of what you are doing or you haven't really organized your code properly.



    Some places still do. I know, I had to survive it here. Those 2 and a half weeks were a true pain in the arse.
  • (cs) in reply to Zahlman
    Anonymous:
    Although in C++ you would probably solve the problem with RAII. And personally, I've never run into a need for anything like this (which is always the common example) "in all my years".

    Your solution uses extra variables for no reason.  The goto code does the same thing, but with less memory.  Maybe memory doesn't matter for your code, but sometimes it matters a lot.  That's why OS kernels are still written in C, even today!

    If you haven't personally needed to write code like that, then go ahead and don't use goto.  Just don't extrapolate from "you" to "everybody."

  • David (unregistered) in reply to P-Nuts

    i = 0;

    j=0;

    done = false;

    while (i <N P !done){< &&>

       while (j < N && !done){

          //do stuff that might not need all the i,j combinations

          j++;

       }

       i++;

    }

     

    no gotos, no extra variable, and maintains readability.  IMO it improves readability over the goto version because all logic determining program flow is on two lines, the two while statements, rather than three, two for statements and a goto.

          

  • David (unregistered) in reply to David

    bah, that cut off the first while loop.  That should be (i less-than N && !done)

  • Masklinn (unregistered) in reply to Pradeep Padala
    Anonymous:
    Alex Papadimoulis:

    Everybody knows that you should never use "goto" statements. Well, except in one or two rare circumstances that you won't come across anyway. But even when you do come across those situations, they're usually "mirage cases" where there's no need to "goto" anyway.


    Before writing such commentary, think about kernel code where gotos are used extensively. See, http://kerneltrap.org/node/553.

    The Linux kernel is a very precise (although huge) snippet of code that is both in C (meaning no exceptions) and in need of very high performances (meaning once again no exceptions even if they were avaible).

    The kernel is NOT the rule, it's the exception, and it's built by highly educated people with high understanding of the language, it's features and their implications.

    Not many people will ever submit a patch to the kernel itself, therefore is Alex's double barrier (you won't encounter many case where you need gotos and usually - but not always - these won't even be) perfectly valid and acceptable

    dubwai:
    I feel pretty confident that given a piece of code that uses gotos, I can structure the code in a way that doesn't use them and is at least 'as good'.

    No, from an efficiency point of view an exception (which is what is usually needed to replace a well used goto) is VERY expensive, codes requiring very high levels of performance (such as the aforementioned Linux kernel) can't run without gotos, because the performance hit would be as horrible as unjustified.

    Now if you want to have fun, you may try rewriting the kernel in C++ using exceptions, but you won't ever get anywhere close the the C version's performances.

  • tobermory (unregistered) in reply to P-Nuts

    Personally, I consider exceptions an ugly kludge, as bad as poorly used gotos. They're effectively a COMEFROM statement.

    When you write code like this:
    try
    {
     doSomethingRisky();
    }
    catch( BorkedException b )
    {
     flagError();
    }
    you have no idea precisely what part of the code that may throw exceptions called by doSomethingRisky has triggered the flagError unless you have a unique exception for each failure. And even if you do, you end up with something every bit as nasty as directly calling gotos from the code under doSomethingRisky into labels in the position of the catch block.

    Everything you call from your function can potentially be an exit point, bypassing the function entirely. You want to clean up? Tough. Let's consider the doSomethingRisky() function in more depth.
    void doSomethingRisky()
    {
     allocateThreadResources();
     getFileLock();
     doStuff();
     unlockFile();
     deallocateResources();
    }
    If getFileLock throws an exception, the thread resources never get deallocated. If doStuff throws an exception, the file remains locked and the thread resources are never deallocated. And even if it's unlockFile that fails, you won't get the chance to deallocate those thread resources.

    Worse, if it fails, it fails invisibly. There is no indication in this function whether the errors are being handled at some higher level or not. Looking at that code doesn't tell you if there's error handling or not; it's quite possible that every failure has its own exception that cleans up appropriately, but it's not visible at this level.

    Personally I prefer error handling of the form:

    int errorCode = doSomethingRisky();
    switch( errorCode )
    {
    BAD_THING_ERROR:
     flagBadThingError();
     break;
    ...
    }

    Because error codes have to be visibly propagated up the function tree, you can tell at a glance whether a function has implemented error handling or not. If a function dies without achieving its aims, control returns to the function that called it and doesn't go to the exception code, and a simple test of the return value lets you know whether or not you have to clean up.

    But I'm not usually working with mission-critical code. If every function I was going to call had an unacceptable chance of failure and the need to clean up a heap of things after it, nesting conditionals to handle it this way would make the code hideous. Far better would be to adopt the Linux kernel modus operandi:

    int doSomethingRisky()
    {
     if(!allocateThreadResources())
     {
      goto threadAllocateFailure;
     }
     if(!getFileLock())
     {
      goto fileLockFailure;
     }


     doStuff();

    fileLockFailure:
     unlockFile();
    threadAllocateFailure:
     deallocateThreadResources();
    }

    Less code, no nesting and silly indentation, and probably the most transparent way of achieving that flow control possible. It's efficient from a low-level perspective, too.

    I hope this convinces some people at least that gotos have their place.

    NB: this forum software is hideous :P
  • Azumanga (unregistered) in reply to dubwai

    Quote

    The thing about the 'gotos are sometimes necessary' argument is that Java doesn't allow gotos yet I've never heard of a single one of the thousands of Java projects failing for the want of a goto.

    While java doesn't have a goto, it does have a break which will take a label, and then goto that label, which to be honest is goto really in all but name (and the places where java lets you use it are the only places I ever use goto)

  • (cs) in reply to Azumanga
    Anonymous:
    While java doesn't have a goto, it does have a break which will take a label, and then goto that label, which to be honest is goto really in all but name (and the places where java lets you use it are the only places I ever use goto)


    Heh... that's like saying += is ++ in all but name because you only ever += by 1 :)
  • (cs) in reply to tobermory

    Anonymous:
    Personally, I consider exceptions an ugly kludge, as bad as poorly used gotos. They're effectively a COMEFROM statement.

    When you write code like this:
    try
    {
     doSomethingRisky();
    }
    catch( BorkedException b )
    {
     flagError();
    }
    you have no idea precisely what part of the code that may throw exceptions called by doSomethingRisky has triggered the flagError unless you have a unique exception for each failure. And even if you do, you end up with something every bit as nasty as directly calling gotos from the code under doSomethingRisky into labels in the position of the catch block.

    Everything you call from your function can potentially be an exit point, bypassing the function entirely. You want to clean up? Tough.

    I'm guessing that you are not aware of:

    try {
      // do something
    } catch (Exception e) {
      // somthing happened
    } finally {
      // always executes
    }

    In any event, your approach is not safe because it assumes you know everything that can go wrong.  Specifically it makes a lot of assumptions that things won't go wrong.

  • (cs) in reply to Azumanga

    Anonymous:
    Quote ---- The thing about the 'gotos are sometimes necessary' argument is that Java doesn't allow gotos yet I've never heard of a single one of the thousands of Java projects failing for the want of a goto. ---- While java doesn't have a goto, it does have a break which will take a label, and then goto that label, which to be honest is goto really in all but name (and the places where java lets you use it are the only places I ever use goto)

    Why do people insist on calling structured statements gotos?  An 'if' is a goto really in all but name.  Gotos allow you to jump into the middle of loops or if statements from completely unrelated blocks of code.  There is no feature in Java that allows this.

  • (cs) in reply to Quinnum
    Quinnum:

    Actually Java does have gotos, as does the JVM. It just wont allow you to compile it.

    Run a class binary through a de-compiler and enjoy the gotos in the resulting code.

    Java has a keyword goto that is not allowed.  Therefore there is no Java program with gotos.  The bytecodes are like assembly of machine instructions.  They have nothing to do with this discussion and I'm puzzled as to why you would think this was a relevant point.

  • (cs) in reply to tobermory

    Anonymous:
    Personally, I consider exceptions an ugly kludge, as bad as poorly used gotos. They're effectively a COMEFROM statement.

    Personally I think that is a really stupid stance.  I've worked with a lot code like that and it's never reliable.

    Anonymous:
    If getFileLock throws an exception, the thread resources never get deallocated. If doStuff throws an exception, the file remains locked and the thread resources are never deallocated. And even if it's unlockFile that fails, you won't get the chance to deallocate those thread resources.

    try/catch/finally

    Anonymous:

    Worse, if it fails, it fails invisibly. There is no indication in this function whether the errors are being handled at some higher level or not. Looking at that code doesn't tell you if there's error handling or not; it's quite possible that every failure has its own exception that cleans up appropriately, but it's not visible at this level.

    And how does a return code tell you that errors are being handled?

    Anonymous:

    Personally I prefer error handling of the form:

    int errorCode = doSomethingRisky();
    switch( errorCode )
    {
    BAD_THING_ERROR:
     flagBadThingError();
     break;
    ...
    }

    Because error codes have to be visibly propagated up the function tree, you can tell at a glance whether a function has implemented error handling or not. If a function dies without achieving its aims, control returns to the function that called it and doesn't go to the exception code, and a simple test of the return value lets you know whether or not you have to clean up.

    First, checked exceptions in Java are not invisible.  You have to do something about them in your code.

    Secondly, what happens to your code when someone adds a new return code?  How about all the other places that you used this redundant piece of tedious code?

Leave a comment on “When It's OK To GOTO”

Log In or post as a guest

Replying to comment #:

« Return to Article