Like Tree10Likes

I used my first goto statement today!

This is a discussion on I used my first goto statement today! within the General Discussions forums, part of the Community Boards category; Why would I use the black art known as goto? Well, I had some code like this, Code: for (auto ...

  1. #1
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    1,283

    I used my first goto statement today!

    Why would I use the black art known as goto? Well, I had some code like this,

    Code:
    for (auto it = vec.begin(); it < vec.end(); ) {
    
    for (int i=0; i<4; i++) {
       if (t[i]) {
    
          switch (x) {
    
             case 23 :
                /* needed to go back to iterating through my vector here */
          }
       }
    }
    }
    Turns out, I completely forgot that 'break' will break you out of a switch before a loop so I just put a label or w/e before my int for-loop and omg, my code actually works properly now or at least, I have successfully replaced recursion with iteration.

    Sorry, I just felt like sharing. goto's are actually pretty sweet. I'm going to needlessly fill my code with them from now on.

  2. #2
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,676
    Hmm...not the worst use of a goto, but probably not the best either. There have been times that I really wish C supported labeled break/continue statements, but that's becoming rare. I am more than happy to simply move stuff into helper functions:
    Code:
    for (auto it = vec.begin(); it < vec.end(); ) {
        process_vector_elements()
    }
    ...
    void process_vector_elements()
    {
        for (int i = 0; i < 4; ++i) {  // use pre-increment in C++
            if (t[i]) {
                switch (x) {
                    case 23:
                        return;
    When it hits that return, and process_vector_elements is done, the vector loop will proceed to the next element.

    As for the pre-increment, it avoids having to call a copy constructor (since it doesn't have to save the old value of i).

    The other nice thing about splitting up your function like this is you've separated the processing of a vector of objects, from the processing of a single object. That keeps your code loosely coupled and makes it easier to do things like unit test your code, since you have isolated the processing of a single foo object. And it will keep your code (or at least this part of it) goto-free.

    Also, I hope you're not using magic numbers (4 and 23) in the real code. Some #defines and enums would do nicely here.

    Last note:
    This definitely seems to be C++. I'm not much of a C++ expert, but I can't recall ever seeing a good use of goto in C++. In C, however, the most common use that I've seen (and personally the only one I generally think is a good use of goto) is for error handling, so you can avoid deeply-nested if statements or repeating tedious cleanup code.
    laserlight likes this.

  3. #3
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    1,283
    How does your method affect the call stack though? The only reason I ask is because I have recursive code that overflows the stack with calls. So today I made it iterative and not recursive.

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    22,269
    Quote Originally Posted by MutantJohn
    How does your method affect the call stack though?
    It is just one more function call, obviously.

    Quote Originally Posted by MutantJohn
    The only reason I ask is because I have recursive code that overflows the stack with calls. So today I made it iterative and not recursive.
    That is no reason to avoid the appropriate use of functions. If necessary, use an explicit stack.
    MutantJohn likes this.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    1,283
    Whoa, whoa, whoa, I can use an explicit stack? How do I do that? 'Cause here's my functions, delaunay(), two_to_three().

    My code goes delaunay() calls two_to_three() calls delaunay() calls two_to_three().... And this goes on for apparently, a sufficiently large amount. How do I manually allocate a call stack?

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    22,269
    Well, you need to figure out what data needs to be saved so you can create stacks of the appropriate type(s). So, you have this code that does the stuff in delaunay. When you need to do the stuff that is currently in two_to_three (or when you need to call delaunay in two_to_three), you save the data required for delaunay in its stack. At some point, you will reach what was the base case for the recursion, upon which you pop from the appropriate stack and do whatever processing is left to do, then pop from the other stack, etc.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Quote Originally Posted by anduril462 View Post
    Hmm...not the worst use of a goto, but probably not the best either. There have been times that I really wish C supported labeled break/continue statements, but that's becoming rare. I am more than happy to simply move stuff into helper functions:
    Code:
    for (auto it = vec.begin(); it < vec.end(); ) {
        process_vector_elements()
    }
    ...
    void process_vector_elements()
    {
        for (int i = 0; i < 4; ++i) {  // use pre-increment in C++
            if (t[i]) {
                switch (x) {
                    case 23:
                        return;
    When it hits that return, and process_vector_elements is done, the vector loop will proceed to the next element.

    As for the pre-increment, it avoids having to call a copy constructor (since it doesn't have to save the old value of i).

    The other nice thing about splitting up your function like this is you've separated the processing of a vector of objects, from the processing of a single object. That keeps your code loosely coupled and makes it easier to do things like unit test your code, since you have isolated the processing of a single foo object. And it will keep your code (or at least this part of it) goto-free.

    Also, I hope you're not using magic numbers (4 and 23) in the real code. Some #defines and enums would do nicely here.

    Last note:
    This definitely seems to be C++. I'm not much of a C++ expert, but I can't recall ever seeing a good use of goto in C++. In C, however, the most common use that I've seen (and personally the only one I generally think is a good use of goto) is for error handling, so you can avoid deeply-nested if statements or repeating tedious cleanup code.
    I don't get it. So instead of using goto in a proper, structured manner, you've resorted to breaking up a loop in a completely unnatural way (nevermind the issue of having clean access to variables from the calling function)? It's really very simple:

    Code:
        for( ;; )
            for( ;; )
                goto done;
        done:;
    Or even:

    Code:
        try
        {
            for( ;; )
                for( ;; )
                    throw 1;
        }
        catch( int )
        {    }
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  8. #8
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    1,283
    Quote Originally Posted by laserlight View Post
    Well, you need to figure out what data needs to be saved so you can create stacks of the appropriate type(s). So, you have this code that does the stuff in delaunay. When you need to do the stuff that is currently in two_to_three (or when you need to call delaunay in two_to_three), you save the data required for delaunay in its stack. At some point, you will reach what was the base case for the recursion, upon which you pop from the appropriate stack and do whatever processing is left to do, then pop from the other stack, etc.
    Okay, I'ma describe my code in more detail and hopefully you can help me figure this out. Or someone else can.

    The idea is, I'm taking a stack of tetrahedrons and testing them. When one fails, I have to remove it and add a few more to the stack. Hence the two_to_three. I erase 2 and stack 3 more. This can happen a lot.

    I don't know if stacks and recursion are the way to do this though. I spent this afternoon replacing the recursion with iteration and so far, my code seems to be behaving stably even though my treewalk is encountering some interesting errors for really high particle counts (32^3 vertices being triangulated).

    I'll think about it but iteration seems kind of like the way to go.

  9. #9
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,676
    Quote Originally Posted by Sebastiani View Post
    I don't get it. So instead of using goto in a proper, structured manner, you've resorted to breaking up a loop in a completely unnatural way (nevermind the issue of having clean access to variables from the calling function)?
    Yes, you don't get it. I'm not breaking it up in a completely unnatural way. In fact, it's a very natural way to break up the code, along an "object boundary". Allow myself to quote myself:
    Quote Originally Posted by anduril462 View Post
    The other nice thing about splitting up your function like this is you've separated the processing of a vector of objects, from the processing of a single object. That keeps your code loosely coupled and makes it easier to do things like unit test your code, since you have isolated the processing of a single foo object. And it will keep your code (or at least this part of it) goto-free.
    That is probably the most important point I made in my post. Decoupling your code like that is a fundamental component of good software design. Such design choices become more important as you work on larger and larger projects. Tight coupling makes it more difficult to test and debug your code, and reason about it, since you must consider the whole, tightly-coupled mess of code, instead of small, self-contained bits of code.
    Quote Originally Posted by Sebastiani View Post
    It's really very simple:
    Code:
        for( ;; )
            for( ;; )
                goto done;
        done:;
    Yep. It's so simple, even I can figure out how to use goto in places I don't need to (and shouldn't) use it.
    Quote Originally Posted by Sebastiani View Post
    Or even:


    Code:
        try
        {
            for( ;; )
                for( ;; )
                    throw 1;
        }
        catch( int )
        {    }
    Now, that didn't occur to me. But to be fair, I did profess my lack of C++ expertise. It still, however, maintains unnecessarily tight coupling between the object itself and the list of objects.

  10. #10
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,409
    Sorry, I just felt like sharing. goto's are actually pretty sweet. I'm going to needlessly fill my code with them from now on.
    O_o

    I get that this is hyperbole, but you managed your first `goto' in a language with scoped destruction and real world compilers that don't always follow the standard.

    Now, I'm not saying this will cause you problems here (The chosen example is fixed construct really so no different than `break'.), but pretty much every compiler I've ever worked on has produced invalid code on complicated `goto' and similar constructs.

    To be clear, the standard specifically mandates that destructors be called however execution leaves a block, but with calculated `goto', or functionality that is otherwise problematic to statically analyze, most compilers aren't perfect.

    As usual when this comes, my absurdly broad experience with breaking compilers, I am only making you aware of the potential issue you may wish to avoid on the off chance as the situation I reference is despicably hard to debug.

    The only reason I ask is because I have recursive code that overflows the stack with calls. So today I made it iterative and not recursive.
    Well, you've already made it iterative, and, as I infer from the example, the major iteration is broken over partial iteration.

    Your problem is literally already solved; you only need to massage the partial iteration into a consumable form.

    You could use a dumb `continue' function, but I'd recommend continuing with the iterator pattern:

    Code:
    typedef /* whatever */ elem;
    // ...
    bool yield(elem)
    {
        switch(elem)
        {
            /* partial logic */
            case 23: return false;
            // ...
        }
    }
    // ...
    for(/*major*/)
    {
        partial<elem> inner(it, vec.end(), yield)
        partial<elem> end;
        // ...
        while(inner < end)  
        {
            // ...
            // use *inner as it[i]
            // ...
        }
    }
    // ...
    Of course, to me nothing is more natural than processing collections iteratively; your mileage may vary.

    The idea would be the same without employing the iterator pattern so heavily:

    Code:
    typedef /* whatever */ elem;
    // ...
    bool continue(partial & it, partial end, int & counter)
    {
        switch(/* *it[counter] */)
        {
            /* partial logic */
            case 23: return false;
            // ...
        }
    }
    // ...
    for(/*major*/)
    {
        int counter(0);
        while(continue(it, end, counter))  
        {
            // ...
            if(valid(it))
            {
                // whatever
            }
            else
            {
                // remove from/insert into vec at it [two_to_three(it, end)];
                // or whatever again using iterators from the partial process
            }
            // ...
        }
    }
    // ...
    [Edit]
    Yes, I realize `continue' is a keyword; I chose it for the sake the familiar meaning not suggesting this is actual code.
    [/Edit]

    These are just examples of course, but crucially to the concept, you would not have "resorted to breaking up a loop in a completely unnatural way"; the core of the suggestion is breaking up the loop into the basic components of, in this case partial, iteration: increment and access.

    The incrementing logic isn't just your `++'; the logic of iteration is bent by the conditions `i<4' and `it < vec.end()' so these conditions necessarily change the nature of the `continue' function.

    Thanks to the dance of incrementing your access remains normal and trivial: `*it'.

    Obviously, because you process the collection as a collection over major iteration, the partial processes, for example, deciding what three tetrahedrons to add, so returning a set of tetrahedrons, is a process treated as a separate component of processing the collection without managing the collection.

    To be clear, no one has suggested arbitrarily breaking your code up into functions and passing around every stack variable as a reference. The goal isn't to do exactly what you've done in a different way; you must refactor, rewrite, the code so that the objects belonging to the collection are "fed", as necessary, into several district components each operating on as few as necessary tetrahedrons from the collection to do the job while the major loop drives how the functions are "fed" and what to do with the results.

    I hesitate to call this a "state machine", but the label isn't far off.

    Code:
    try
    {
        for( ;; )
            for( ;; )
                throw 1;
    }
    catch( int )
    {    }
    This example should never have been suggested.

    @MutantJohn: I promise you, you would be fired for writing that C++ at any of the places I've worked.

    Soma
    “Often out of periods of losing come the greatest strivings toward a new winning streak.” -- Fred Rogers
    “Salem Was Wrong!” -- Pedant Necromancer

  11. #11
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    1,283
    Now I'm just sad...

  12. #12
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Quote Originally Posted by anduril462 View Post
    Yep. It's so simple, even I can figure out how to use goto in places I don't need to (and shouldn't) use it.
    And why exactly is that?

    Quote Originally Posted by phantomotap
    This example should never have been suggested.

    @MutantJohn: I promise you, you would be fired for writing that C++ at any of the places I've worked.
    Please do enlighten me, O' self-righteous one.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  13. #13
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,409
    Please do enlighten me, O' self-righteous one.
    O_o

    I've already told you.

    (See the time you attempted to transform exceptions into references to be conditionally checked for "nullness".)

    In short, you are using the exception mechanism poorly while making the code more complicated for absolutely no reason without meaningfully changing the problem of perception you are attempting to correct.

    Soma
    “Often out of periods of losing come the greatest strivings toward a new winning streak.” -- Fred Rogers
    “Salem Was Wrong!” -- Pedant Necromancer

  14. #14
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Quote Originally Posted by phantomotap View Post
    O_o

    I've already told you.

    (See the time you attempted to transform exceptions into references to be conditionally checked for "nullness".)

    In short, you are using the exception mechanism poorly while making the code more complicated for absolutely no reason without meaningfully changing the problem of perception you are attempting to correct.

    Soma
    I think you're mixing up my "null reference" and "unchecked return value throws exception" experiments. In any case, I don't see how throwing an exception is such a "poor" choice. Exceptions make great control flow constructs.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  15. #15
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    1,283
    I'd also like to mention that if I was immediately fired for using a goto without being told to at least go back and rewrite my code then that'd be a very stressful job.

    phantom, the reason why a goto was used was because it was a simple solution to integrate and yes, it's pedantic. I can't imagine using a non-ISO compliant compiler. People know g++ is free, right? Also, if a goto was the bane of all existence then why did the developers spend time creating it?

    I think the first reply in this thread was right. It's not the best use of a goto but it's not bad. It doesn't break my code and for once, I can actually triangulate 8000 points whereas before I couldn't triangulate 4000 not using goto as a means of iteration. I like using goto in this context because it doesn't obfuscate my code from what it was before. Now, I will look into the use of stacks but in the meantime, a goto seems to suffice just fine. I will analyze the time it would take to rework the structure of my code and see if it's exactly that much better of an implementation to use stacks.

    Also, my code really looks likes this :
    Code:
    for (auto it = fract.begin(); it < fract.end(); ) {
    
       test :
    
       tetra *t = *it;
    
       for (int i=0; i<4; i++) {
    
          if (t->ngb[i]) {
    
              auto distance = std::distance(fract.begin(), it);
    
              switch (x) {
    
                   case 23 :
    
                         if (two_to_three(fract, inherit, t, t->ngb[i], plane, x, y) == 0)
                            it = fract.begin() + distance;
                         else
                            it++; 
                         if (it >= fract.end()) return;
    
                        goto test;
               }
          } if (i == 3) it++;
       }
    }
    This way, it's a bit more clear to see what I'm doing. I don't know... A goto was a simple solution to a problem that presented itself and so far, my code seems stable. You can use non-compliant compilers all you want and I'm not guaranteeing that my code will work. But as far as I can tell, the -pedantic flag is silent during compilation so it's ISO cleared and if you're compiler isn't ISO compliant then whose fault is that? I can't write for every possible scenario or odd compiler you use. Hopefully the VB compiler is ISO compliant...
    Last edited by MutantJohn; 10-16-2013 at 01:58 AM.

Page 1 of 3 123 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. goto statement help
    By lscamaro in forum C Programming
    Replies: 13
    Last Post: 10-25-2012, 12:47 AM
  2. Trouble using GOTO with the SWITCH statement!
    By xxJaRxx in forum C Programming
    Replies: 9
    Last Post: 03-10-2010, 06:42 AM
  3. GOTO statement in c++
    By kibestar in forum C++ Programming
    Replies: 8
    Last Post: 03-22-2009, 08:10 PM
  4. goto statement interpretation in VC 6 and 7
    By w262 in forum C++ Programming
    Replies: 2
    Last Post: 02-28-2005, 10:37 PM
  5. Goto statement
    By _JjC:: in forum C Programming
    Replies: 2
    Last Post: 03-02-2003, 10:43 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21