views:

325

answers:

5

I came up with the following options:

Using the goto statement:

Start:
    goto Data
Data:
    goto Finish
Finish:
    ;

using the switch statement:

switch(m_state) {
 case State.Start:
  m_state = State.Data;
  break;
 case State.Data:   
  m_state = State.Finish;
  break;
 case State.Finish:
  break;
}

using goto and switch together:

switch(m_state) {
    case State.Start:
     goto case State.Data2;
    case State.Data1:
     goto case State.Finish;
    case State.Data2:
     m_state = State.Data1;
     //call to a function outside the state machine
     //that could possibly change the state
     break;
    case State.Finish:
     break;
}

I prefer the first option using the goto statement, because it is faster and less verbose. But i'm not sure if it's the best option. Performance wise maybe, but when it comes to readability i don't know. That's why i ask this question. Which option do you prefer and why?

A: 

Personally i prefer second one with goto since first one will require unnecessary loop step (for example) to go to the new state

Trickster
+2  A: 

The advantage with the switch over the goto is that you have the state in a variable, not just in the instruction pointer.

With the goto method the state machine has to be the main loop that controls everything else, because you can't step out of it because you would lose the state.

With the switch method the state machine is isolated, and you can go anywhere you want to handle events from the outside. When you return to the state machine, it just continues where yuu left off. You can even have more than one state machine running side by side, something that is not possible with the goto version.

I'm not sure where you are going with the third alternative, it looks just like the first alternative with a useless switch around it.

Guffa
Good point. Also changed the third alternative a bit.
Niek H.
@Niek: Well, now it makes a little more sense. Still, I would rather go for the second alternative. The third alternative has two different states, one in the variable and one in the instruction pointer, and you could easily get into the wrong state. You can go from one state to the other without changing the state variable, so if you leave the loop and reenter, it would return to the previous state.
Guffa
A: 

I prefer mutually calling/recursive functions. To adapt your example:

returnvalue Start() {
    return Data();
}

returnvalue Data() {
    return Finish();
}

returnvalue Finish() {
    …
}

Theoretically, this can be completely inlined so that the compiler output is equivalent to your goto solution (hence, same speed). Realistically, the C# compiler /JITter probably won’t do it. But since the solution is vastly more readable (well, IMHO), I would only replace it with the goto solution after a very careful benchmark proving that it is indeed inferior in terms of speed, or that stack overflows occur (not in this simple solution but larger automata run into this problem).

Even then, I would definitely stick to the goto case solution. Why? Because then your whole messy goto pasta is well-encased inside a block structure (the switch block) and your spaghetti won’t mangle the rest of the code, preventing Bolognese.

In conclusion: the functional variant is clear but in general prone to problems. The goto solution is messy. Only goto case offers a halfway clean, efficient solution. If performance is indeed paramount (and the automaton is the bottle neck), go for the structured goto case variant.

Konrad Rudolph
can't remember c# does tail recursion so I would definitely advice against this method
Toad
reinier: **first** off: while C# doesnt’t do TCO, the JITter does it in some situations: http://blogs.msdn.com/davbr/pages/tail-call-jit-conditions.aspx – **second** Don’t over-estimate the cost of a non-virtual (static?) method call. **third** That’s why I advised for benchmarks. In summary, how this answer deserves to be downvoted is beyond me. It offers a quite complete discussion of the question and its solutions.
Konrad Rudolph
konrad: a lot of maybes. If you don't know the jitter will optimize it, it means you can not use it. Having a stack which slowly runs full is really something you do not want. And it doesn't matter how fast or slow this happens. So I understand the downvote. It's a great solution in a functional language with tail recursion support , but in c# it's bad advice.
Toad
@reinier: **one** maybe. Easily answered by profiling and/or testing. I agree about the necessity of avoiding a stack overflow (notice: cannot ever happen in the example automation) but unfortunately not about anything else. I can’t see how it’s “bad advice” seeing as I have explicitly mentioned that this route cannot be taken *if* these problems exist (again: the example automaton is at no risk of *ever* overflowing the stack, since there is no recursion). For C# as for every language, a *maintainable* solution is good advice. Big automaton coded in `goto`s? Not maintainable.
Konrad Rudolph
Using functions even with tail recursion is not an option, because of the performance. The overhead although small is still too big. Anyway based on your answer(last paragraph) i now prefer the third alternative.
Niek H.
konrad: I see your point, but the example automaton given is probably oversimplified. And even if an automaton is not recursive, if it would 'live' long enough doing enough tail recursions, stack overflows will happen. Of course one can profile this, but who knows in situation 1 on hardware 1 it might work, while in situation 2 where the automaton path takes 2 steps longer it will crash. My point is that unless it is for really simple state machines which exit really fast, this is not something you want to promote
Toad
+2  A: 

If you ever want to break your state machine transition logic into separate functions, you can only do it using switch statements.

switch(m_state) {
        case State.Start:
                m_state = State.Data;
                break;
        case State.Data:                        
                m_state = ComputeNextState();
                break;
        case State.Finish:
                break;
}

It is also more readable, and the overhead of the switch statement (versus Goto) will only make a performance difference in rare circumstances.

EDIT:

You can use "goto case" to make small performance improvement:

switch(m_state) {
        case State.Start:
                m_state = State.Data; // Don't forget this line!
                goto case State.Data;
        case State.Data:                        
                m_state = ComputeNextState();
                break;
        case State.Finish:
                break;
}

However you run the risk of forgetting to update the state variable. Which might cause subtle bugs later on (because you assumed that "m_state" was set), so I would suggest avoiding it.

cdiggins
Notice that usage of `goto case` isn’t the same as “mixing `goto` with `switch`”, it doesn’t have the same disadvantages – in fact, it produces clear, *structured* code and is actually a good solution.
Konrad Rudolph
I added an explanation of why I don't like goto case mixed with switch statements.
cdiggins
+2  A: 

There is a 4th option.

Use an iterator to implement a statemachine. Here is a nice short article showing you how

It has some disadvantages though. Manipulating the state from outside of the iterator is not possible.

I'm also not sure if it is very quick. But you can always do a test.

Toad
I've seen this option. But it's too slow. Switch and goto are the way to go when performance matters.
Niek H.
I wish people would not do this. Iterators were designed to implement iterators; that they do so by building a state machine is an implementation detail. When you have code that looks like an iterator but is in fact something entirely different, it's hard to read, understand, maintain and debug. If you want to build a state machine, then build something that looks like a state machine.
Eric Lippert
@Eric Lippert, why does it matter how you build a state machine? what technique you use? after all the behavior of iterators is well defined, and it can certainly be advantageous sometimes to use constructs widely used in the framework (like IEnumerable<T>) to accomplish a certain goal (IE this automatically also gives you the power of linq as a bonus) (Afterall IEnumerable<T> is nothing more than a state machine at a fundamental level (MoveNext(); get_Current;MoveNext(); get_Current ... )
Pop Catalin
the CCR also has a great use for the iterators, to easy asynchronous programming. Something not typically done via iterators, but it works very well. Thinking outside of the box
Toad