The final word on GOTO (Don't I wis

Blair P. Houghton bph at buengc.BU.EDU
Sat Oct 7 11:36:32 AEST 1989


Rich Salz pointed out (via email) that I'd made a mistake in
my rearrangement of the switch/goto construct currently under
debate.  I've come up with an idea I'd like to see rent asunder
by the people I know will do it best.  :-)

(BTW, I happen to think there's absolutely nothing wrong with goto's,
so long as you think in terms of structure and use the goto's only when
they appear perfectly apt.)

A correct functionality, as Rich showed it, without gotos:

        is_doxu = 0;
        switch(format_char) {
            case 'c': stuff; break;
            case 's': stuff; break;
            case 'd': the stuff I put under d; is_doxu++; break;
            case 'o': the stuff I put under o; is_doxu++; break;
            case 'x': the stuff I put under x; is_doxu++; break;
            case 'u': the stuff I put under u; is_doxu++; break;
        }

        if (is_doxu) {
            stuff
        }

This is probably version requiring least effort to maintain.  In order
to add or delete cases in the switch, you only have to copy any other
case and change the case-constant to fit the new case.  The way I
showed, you have to do it the same in both switches, so it's nominally
more difficult.

The way I'd probably do it if I had tried it from scratch is to
in-my-head expand the "is_doxu" logic and do

	switch(format_char) {
		case 'c': stuff under c; break;
		case 's': stuff under s; break;
		case 'd': stuff under d; break;
		case 'o': stuff under o; break;
		case 'x': stuff under x; break;
		case 'u': stuff under u; break;
        }

        if ( c == 'd' || c == 'o' || c == 'x' || c == 'u' ) {
		stuff for doxu cases
	}

Switch provides a method of conditionally splitting the flow-graph,
but there doesn't exist a dual of it that would describe the joining of
some subset of the split branches of the flow-graph.

Something like

	switch(fc) {
		case 'c': stuff under c; break;
		case 's': stuff under s; break;
		...
		case 'u': stuff under u; break;

		cases 'd', 'o', 'x', 'u': stuff for doxu cases; break;
		cases 'd', 'c', 'u': stuff for dcu cases; break;
	}

and let the compiler figure out how to keep track of the flow (which
obviously can get very very complicated very quickly) of the machine
language, which only knows from JUMP, JNZ, etc., anyway...

Notice that with gotos you could use maybe two or three common-cases,
and then only if you added a lot of extra control structures to track
the flow.  By adding the "cases" keyword, you have the compiler do it
in whatever way its graph-reduction and conflict-arbitration routines
decide is the best.

I haven't really considered order-of-evaluation.  This is a
high-level, low-baked concept at the moment.  The obvious method
is to just replace every "cases 'x','y'" with the equivalent of
"if ( (fc=='x') || (fc=='y') )".  This gets rid of the common
implementation that case-labels will be treated as goto-targets,
but that's an assumption, anyway.  Nothing stops a compiler from
just using a skein of "if...then...else-if...then...else-if...then...else"
constructs as a translation for the switch.

The alternative is to turn common code into a subroutine, and
insert its invocation immediately before the machine-code
construct that implements the break keyword in each branch.

Given that there are a number of programming tools being researched
that use a _graphical_ interface to code a program (i.e., you just
type a few logical statements into a box that looks like a flowchart
symbol and the computer writes the actual C code for you; create a
few arrows connecting boxes and you have flow-of-control) something
like "cases" would be very handy.  Then again, a the computer doing
the coding wouldn't have any compunction in using gotos all over the
place.

What we're doing here (in this discussion) is optimizing structurally,
for the benefit of programmers, while the compiler is obligated to
optimize (most often) for speed first and size second, and readability
of the flow structure is moot.

What we're also doing here (and now) is implementing this desired flow
construct using the current definition of the language.  There's
nothing impossible or ambiguous about using the "if (0) {...}"
pseudo-break to get the job done.  We'll all still get paid for
doing it.

				--Blair
				  "'Stop the Standard!  We have
				   a new keyword to add!' ...

				   :-)

				   You can come down off the
				   ceiling, now, Doug..."

P.S.  Exercise:  Show that allowing multiple "case 'x':" labels
for each constant 'x' would give exactly the results that the "cases" 
keyword provides...the meaning of "break" changes, however, from
"termination of the smallest enclosing while, do, for, or switch," to
"termination of the smallest enclosing while, do, or for; or, jump
to the next matching case-label in the smallest enclosing switch;
or, iff no further cases match, terminate the smallest enclosing switch..."



More information about the Comp.lang.c mailing list