J
James Kanze
Using pejorative descriptors such as "sordid mess" isn't an
argument,
It's not an argument, it's a qualifier to his argument. Note
the use of "spaghetti state machine", later. His point
(perfectly valid, IMHO) is that if the state machine is
structured (not spaghetti), it will lend itself to translation
into structured code, and if it's not, well, you can't make a
silk purse out of a sow's ear, and you're better off fixing the
problem at the source, and redesigning the state machine so that
it's not spaghetti.
although it may be your opinion (e.g., you don't like
licorice). Statement machines regardless of how implemented
are damn pretty compared to ad hoc alternatives.
State machines are a form of a program. They can be structured,
or they can be spaghetti. Most things that are "ad hoc", rather
than designed, will end up spaghetti; a structured state machine
is certainly easier to understand and maintain than spaghetti
coded C or C++.
A common construction of a statement using in C/C++ is a
switch statement with that that "one" integral variable, and
set of cases, right? So the basic form is:
enum state = { ... }
while (true) {
switch(state) {
case state1: ....
break;
case stateN: ...
if(...) state=nextstate;
break;
...
}
}
Somhow I don't see the real coding difference between that
and:
state1:...
...
stateN: ...
if (....) goto nextstate;
It's readable and maintainable. It makes the state transitions
clearer.
But Alf's point is well taken: if the state machine isn't
spaghetti, you can translate it into really nicely structured
code, without even the switch. The switch is useful in two
cases: when the state machine is part of the external
specification (so you can't clean it up), and when it is
constructed dynamically (so you don't actually know what it will
be until runtime).
So I don't understand your objection in terms of code clarity
or maintainability.
[I'd prefer there to be a comment to tell me its a state
machine with both I note the goto-coded variable-less version
is actually less stuff to write, and less stuff to read, so in
fact I think it is clearer. But he amount of code isn't a lot
so that doesn't matter much to me]
If you have modest performance needs, the switch version is a
fine implementation. If you are processing lots of data in
which the tests and operations in state are simple (lexical
analysis, message filtering, ...) the switch version overhead
gets to be interesting and one wants it to go away. (I build
tools that read the source code for large source code bases,
so this matters). Each switch state tests some condition,
and does an assignment to the state variable. It then
(implicitly) jumps back to the beginning of the switch, the
switch does an indirect branch, the processor blows its
pipeline, and then the next state makes progress. The
goto-version doesn't have that extra overhead. So your
alternative is to always choose the slower implementation, to
what end?
Is the extra overhead measurable? I've used the switch a lot,
and I've never found a measurable difference in performance with
respect to using goto. The only difference is that the reader
can clearly see when a state transition occurs. (Another
difference is that it is possible to decide the next state, then
do some sort of additional processing. That can sometimes be
useful.)