tags:

views:

524

answers:

9

The following code was a proof of concept for a message batching routine. Do I avoid goto like the plague and rewrite this code? Or do you think the goto is an expressive way to get this done?

If you'd rewrite please post some code...

var queue = new Queue<TraceItem>(this.batch);
while (this.connected)
{
    byte[] buffer = null;
    try
    {
        socket.Recv(out buffer);
    }
    catch
    {
        // ignore the exception we get when the socket is shut down from another thread
        // the connected flag will be set to false and we'll break the loop
    }

HaveAnotherMessage:
    if (buffer != null)
    {
        try
        {
            var item = TraceItemSerializer.FromBytes(buffer);
            if (item != null)
            {
                queue.Enqueue(item);

                buffer = null;
                if (queue.Count < this.batch && socket.Recv(out buffer, ZMQ.NOBLOCK))
                {
                    goto HaveAnotherMessage;
                }
            }
        }
        catch (Exception ex)
        {
            this.ReceiverPerformanceCounter.IncrementDiagnosticExceptions();
            this.tracer.TraceException(TraceEventType.Error, 0, ex);
        }
    }

    // queue processing code
}
+4  A: 

I guess the goto is SLIGHTLY more readable intuitively... But if you WANTED to avoid it I think all you'd have to do is throw the code in a while(true) loop, and then have a break statement at the end of the loop for a normal iteration. And the goto could be replaced with a continue statement.

Eventually you just learn to read and write loops and other control flow structures instead of using goto statements, at least in my experience.

Platinum Azure
A: 

Umm, I'm not really sure you want to goto out of a try block. I'm pretty sure that is not a safe thing to do, though I'm not 100% sure on that. That just doesn't look very safe...

Michael Dorgan
Would that apply to `continue` inside a `while` loop as well? (See my answer for what I mean)
Platinum Azure
We know that works. I was just worried about breaking out of a critical type section with goto. Seems an area that could easily be buggy.
Michael Dorgan
Jumping out of scope is safe. It's jumping into scope that is unsafe. For this reason, C# does not allow you to jump into scope. Of course, if your critical section is being handled explicitly with function calls in the monitor namespace or whatever, you'll need to put those in a finally block.
Brian
More than fair. Makes sense as well. Thanks.
Michael Dorgan
+30  A: 

Goto will get you into some sticky situations

Pretty much sums up my thoughts on "goto."

Goto is bad programming practice for many reasons. Chief among them is that there is almost never a reason for it. Someone posted a do..while loop, use that. Use a boolean to check if you should continue. Use a while loop. Goto's are for interpreted languages and a call back to assembler days (JMP anyone?). You're using a high level language for a reason. So that you and everyone else doesn't look at your code and get lost.

Josh K
+1 for classic response
SomeMiscGuy
that happened to me earlier today! i'm thinking about writing a little vs2010 extension that sets the computer beeping when you type goto.
jsw
Actually, GOTO statements do have their advantages, provided they are used only when (1) the alternative solution(s) would be less readable or be less intuitive, and (2) it's easy to follow the flow of the program.
Thomas Larsen
For example, compare:--while expression_1 while expression_2 ... if amazing_exception then goto get_out end if end whileend whileget_out:...--with the alternative (which is arguably *less* readable and intuitive):--flag = falsewhile expression_1 while expression_2 ... if amazing_exception then flag = true break end if end while if flag == true then break end ifend while--So many people hate GOTO for no good reason, but it does have its uses---they're just rare. ;-)
Thomas Larsen
@Thomas: I disagree: http://gist.github.com/468178. Tell me that is not readable. How it is more readable to have the program flow jumping in and out of scope?
Josh K
Certainly, in the case of that example, it's more readable to use an exception flag. But compare http://gist.github.com/468890 with http://gist.github.com/468893, or even http://gist.github.com/468894, for example. Now, personally, I'd usually take the latter option---http://gist.github.com/468894---without the GOTO statement---but I do think that GOTO *can* have its time and place. Don't get me wrong--I think that in most situations, using GOTO is the more ineffective or clumsy solution. But structured programming is not just programming minus the GOTO statement, as some people seem to think.
Thomas Larsen
@Thomas: Even in the first example why not simply say `error("{TEXT}"); return;` in that if clause? You're other examples didn't seem like anything you would write if you know at all what you're doing.
Josh K
+1  A: 

Wrap the "HaveAnotherMessage" into a method that takes in the buffer and may call itself recursively. That would seem to be the easiest way to fix this.

JB King
You think making the method recursive will make it better? :\ What's wrong with a simple loop?
Brendan Long
Using recursion to handle a possibly open-ended amount of data coming over a socket doesn't seem like the best idea.
Adam Crossland
@Adam At least no one is suggesting adding a `try..catch(StackOverflowException)` block :)
Michael Stum
@Michael, I really wish that I had.
Adam Crossland
-1: Recursion for open-ended socket reading? That's just asking for trouble! Bad idea!
Platinum Azure
+1  A: 

I would avoid goto in this case, and refactor it. The method reads too long in my opinion.

Mikael Svenson
+12  A: 

Replace the goto with a do-while, or simply a while loop if you don't want the "always run once" functionality you have right now.

var queue = new Queue<TraceItem>(this.batch);
while (this.connected)
{
    byte[] buffer = null;
    try
    {
        socket.Recv(out buffer);
    }
    catch
    {
        // ignore the exception we get when the socket is shut down from another thread
        // the connected flag will be set to false and we'll break the loop
    }

    do {
        if (buffer != null)
        {
            try
            {
                var item = TraceItemSerializer.FromBytes(buffer);
                if (item != null)
                {
                    queue.Enqueue(item);
                    buffer = null;
                }
            }
            catch (Exception ex)
            {
                this.ReceiverPerformanceCounter.IncrementDiagnosticExceptions();
                this.tracer.TraceException(TraceEventType.Error, 0, ex);
            }
        }
    } while(queue.Count < this.batch && socket.Recv(out buffer, ZMQ.NOBLOCK))

    // queue processing code
}
glowcoder
less code + clarity... less sucky and embarrassing. :)
jsw
Imo, I like Josh's answer better.
glowcoder
if you have a single `if (xxx) goto LABEL;` that should be a strong indication that you could rewrite this as a loop
Dolphin
+2  A: 

You could refactor is to something like this.

while (queue.Count < this.batch && buffer != null)
{
    try
    {
        var item = TraceItemSerializer.FromBytes(buffer);
        buffer = null;
        if (item != null)
        {
            queue.Enqueue(item);
            socket.Recv(out buffer, ZMQ.NOBLOCK)
        }
    }
    catch (Exception ex)
    {
        this.ReceiverPerformanceCounter.IncrementDiagnosticExceptions();
        this.tracer.TraceException(TraceEventType.Error, 0, ex);
    }
}
ChaosPandion
+14  A: 

It's so amazingly easy to rid yourself of GOTO in this situation it makes me cry:

var queue = new Queue<TraceItem>(this.batch);
while (this.connected)
{
    byte[] buffer = null;
    try
    {
        socket.Recv(out buffer);
    }
    catch
    {
        // ignore the exception we get when the socket is shut down from another thread
        // the connected flag will be set to false and we'll break the loop
    }
    bool hasAnotherMessage = true
    while(hasAnotherMessage)
    {
        hasAnotherMessage = false;
        if (buffer != null)
        {
            try
            {
                var item = TraceItemSerializer.FromBytes(buffer);
                if (item != null)
                {
                    queue.Enqueue(item);

                    buffer = null;
                    if (queue.Count < this.batch && socket.Recv(out buffer, ZMQ.NOBLOCK))
                    {
                        hasAnotherMessage = true;
                    }
                }
            }
            catch (Exception ex)
            {
                this.ReceiverPerformanceCounter.IncrementDiagnosticExceptions();
                this.tracer.TraceException(TraceEventType.Error, 0, ex);
            }
        }
    }
    // queue processing code
}
Randolpho
+1 despite the fact that this answer was so easy :-)
Jason Williams
+1 for *"makes me cry."*
Josh K
+1 for demonstrating that nearly all GOTOs are just a minute of thought away from death.
Adam Crossland
Heh... Apparently not *that* easy, since I messed up the body of the while loop. Ironically, the original works as intended thanks to the single if statement in the while block.
Randolpho
A: 

Kind of related to Josh K post but I'm writing it here since comments doesn't allow code.

I can think of a good reason: While traversing some n-dimensional construct to find something. Example for n=3 //...

for (int i = 0; i < X; i++)
    for (int j = 0; j < Y; j++)
        for (int k = 0; k < Z; k++)
            if ( array[i][j][k] == someValue )
            {
                //DO STUFF
                goto ENDFOR; //Already found my value, let's get out
            }
ENDFOR: ;
//MORE CODE HERE...

I know you can use "n" whiles and booleans to see if you should continue.. or you can create a function that maps that n-dimensional array to just one dimension and just use one while but i believe that the nested for its far more readable.

By the way I'm not saying we should all use gotos but in this specific situation i would do it the way i just mentioned.

Carlos Muñoz