Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!purdue!bu-cs!buengc!bph From: bph@buengc.BU.EDU (Blair P. Houghton) Newsgroups: comp.lang.c Subject: Re: The final word on GOTO (Don't I wis Summary: I pseudo-flame myself... Message-ID: <4467@buengc.BU.EDU> Date: 7 Oct 89 01:36:32 GMT References: <6396@ficc.uu.net> <725@thor.wright.EDU> <6430@ficc.uu.net> <4447@buengc.BU.EDU> <867@crdos1.crd.ge.COM> <4462@buengc.BU.EDU> Reply-To: bph@buengc.bu.edu (Blair P. Houghton) Followup-To: comp.lang.c Distribution: na Organization: Boston Univ. Col. of Eng. Lines: 122 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..."