chris@trantor.umd.edu (Chris Torek) (03/03/88)
-In article <24996@cca.CCA.COM> g-rh@cca.CCA.COM (Richard Harter) writes: -}As a side point, I like the suggestion that someone made that there be -}a fallthrough statement rather than automatic fallthrough. In article <222965b9@ralf.home> Ralf.Brown@B.GP.CS.CMU.EDU writes: -So, you would like to have to write - switch(var) - { - case foo: fallthrough; - case bar: fallthrough; - case baz: fallthrough; - case mung: /* code to do something for all four cases */ - } While this would work, if we assume D has aggregate constructors, there is a handsomer way: switch (e) { case [1, 2, 3, 5, 7, 13, 19, 23]: ... } /* syntax above is only for demonstration */ -- In-Real-Life: Chris Torek, Univ of MD Computer Science, +1 301 454 7163 (still on trantor.umd.edu because mimsy is not yet re-news-networked) Domain: chris@mimsy.umd.edu Path: ...!uunet!mimsy!chris
mlandau@bbn.com (Matt Landau) (03/03/88)
In comp.lang.c (<2403@umd5.umd.edu>), chris@trantor.umd.edu (Chris Torek) writes: >While this would work, if we assume D has aggregate constructors, >there is a handsomer way: > > switch (e) { > case [1, 2, 3, 5, 7, 13, 19, 23]: ... > } > /* syntax above is only for demonstration */ And of course we add ranges, giving switch(e) { case [1, 2, 3..7, 13, 19, 23..36]: . . . default: . . . } /* what he said */
g-rh@cca.CCA.COM (Richard Harter) (03/03/88)
In article <2403@umd5.umd.edu> chris@trantor.umd.edu (Chris Torek) writes: >-In article <24996@cca.CCA.COM> g-rh@cca.CCA.COM (Richard Harter) writes: >-}As a side point, I like the suggestion that someone made that there be >-}a fallthrough statement rather than automatic fallthrough. >While this would work, if we assume D has aggregate constructors, >there is a handsomer way: > switch (e) { > case [1, 2, 3, 5, 7, 13, 19, 23]: ... > } > /* syntax above is only for demonstration */ Well, yes, one ought to be able to do that. However it is isn't quite as strong as fallthrough, where one can say switch (e) { case foo: some code; fallthrough; case baz: some more code; } In C as it stands now you can do this -- indeed, the complaint is that one can do this unintentionally. If one adds aggregate constructors and takes away automatic fallthrough, it seems to me that you weaken the language. No doubt there are purists that say you shouldn't do the sort of thing given above. I wouldn't go that far, but I would agree that one should be able to use aggregate constructors when cases actually share code. -- In the fields of Hell where the grass grows high Are the graves of dreams allowed to die. Richard Harter, SMDS Inc.
pedersen@acf3.NYU.EDU (paul pedersen) (03/04/88)
In article <25200@cca.CCA.COM> g-rh@CCA.CCA.COM.UUCP (Richard Harter) writes: >Well, yes, one ought to be able to do that. However it is isn't quite >as strong as fallthrough, where one can say > > switch (e) { > case foo: > some code; > fallthrough; > case baz: > some more code; > } > > >In C as it stands now you can do this -- indeed, the complaint is that >one can do this unintentionally. If one adds aggregate constructors and >takes away automatic fallthrough, it seems to me that you weaken the >language. No doubt there are purists that say you shouldn't do the >sort of thing given above. I wouldn't go that far, but I would agree >that one should be able to use aggregate constructors when cases actually >share code. > Fallthrough may be occasionally justifiable. One good example (stolen from Knuth's classic article "Structured programming with go to statements") is a P-code-type interpreter, where the Subtract case may negate one operand and then branch to the Add case. I think that a better D option would be to mark this explicitly with the classic language for marking an "abnormal" transfer of control: switch (op) { case Add: blah-blah-blah case Subtract: negate operand; goto case Add; /* more cases */ } This 'goto' is signalled as exceptional by the reserved word 'case' appearing in place of a label. The use of 'goto' rather than 'fallthrough' is also superior because it is not as fragile if a new case is introduced. I do not think, however, that such a "general" concept as either fallthrough or on-the-fly constructors should be used to support the common use of multiple cases which execute exactly the same code. Here some other syntax is needed. The natural 'case a,b,c:' is defeated by C's comma operator, and 'case a:b:c:' is not LL(1) parseable, but some other C-ish syntax can probably be invented. In current practice, when I need to fall through, I always use the conventional comment /* FALLTHROUGH */ to mark the spot.
cabo@tub.UUCP (Carsten Bormann) (03/06/88)
In article <25200@cca.CCA.COM> g-rh@CCA.CCA.COM.UUCP (Richard Harter) writes: [about a solution to get back some expressive power that will be lost if ``case'' implies ``break'':] () switch (e) { () case foo: () some code; () fallthrough; () case baz: () some more code; () } (Current) C already has a good fallthrough statement. It is called (surprise): goto. switch (e) { case foo: some code; goto baz_case; case baz: baz_case: some more code; } Any optimizer will throw away the superfluous branch implied by the goto. This also has the advantage to use the existing ``spaghetti code warning'' keyword, and to allow you to give a descriptive name for the ``baz_case''. Now if D had a comefrom... -- Carsten Bormann, <cabo@tub.UUCP> <cabo@db0tui6.BITNET> <cabo@tub.BITNET> Communications and Operating Systems Research Group Technical University of Berlin (West, of course...) Path: ...!pyramid!tub!cabo from the world, ...!unido!tub!cabo from Europe only.
g-rh@cca.CCA.COM (Richard Harter) (03/07/88)
In article <401@tub.UUCP> cabo@tub.UUCP (Carsten Bormann) writes: >In article <25200@cca.CCA.COM> g-rh@CCA.CCA.COM.UUCP (Richard Harter) writes: >[about a solution to get back some expressive power that will be lost if > ``case'' implies ``break'':] > >(Current) C already has a good fallthrough statement. >It is called (surprise): >goto. [transferring between cases goto shown.] But, but, but... I had naturally supposed that the goto would be dropped from D. :-) Actually, does anyone use goto's in C to any signifigant extent? Currently I average about one goto per 10,000 lines of code, all of them being transfers to a procedure epilog, e.g foobaz() { .... allocate space and other setup .... if (some_special_condition) goto wrapup; .... wrapup: deallocate space and other cleanup } Almost every language has a goto construct, apparently on the principle "We know you won't use goto's, but there might be a special case where you need one, so here it is - better to be safe than sorry." But do people actually use it to any signifigant extent? Why not just drop it? -- In the fields of Hell where the grass grows high Are the graves of dreams allowed to die. Richard Harter, SMDS Inc.
PEPRBV%CFAAMP.BITNET@husc6.harvard.EDU (Bob Babcock) (03/07/88)
> Almost every language has a goto construct, apparently on the >principle "We know you won't use goto's, but there might be a special >case where you need one, so here it is - better to be safe than sorry." >But do people actually use it to any significant extent? Why not just >drop it? I don't understand. First you give a perfectly reasonable example of the use of goto (which would be awkward to code otherwise), and then you propose eliminating goto from the language because you don't use it very often. One of the reasons I like programming in C rather than (pre-77) Fortran is that you have the flow control constructs necessary to eliminate abusive use of goto's, but you still have goto available for the occasional place where it best expresses what you want to do.
g-rh@cca.CCA.COM (Richard Harter) (03/07/88)
In article <12159@brl-adm.ARPA> PEPRBV%CFAAMP.BITNET@husc6.harvard.EDU (Bob Babcock) writes: >I don't understand. First you give a perfectly reasonable example of >the use of goto (which would be awkward to code otherwise), and then >you propose eliminating goto from the language because you don't use >it very often. One of the reasons I like programming in C rather than >(pre-77) Fortran is that you have the flow control constructs >necessary to eliminate abusive use of goto's, but you still have >goto available for the occasional place where it best expresses >what you want to do. My apologies for muddying the waters. Yes, there are rare occasions where a goto lets you build by hand a construct that is not available in the language. Most of these instances reflect deficiencies in the language. In the example I cited the deficiencies are: (a) C has two escape statements, break and return. 'return' escapes from the outermost containing block (the function). 'break' is a little ambiguous. By analogy one would expect it to escape from the innermost containing block. Actually, of course, it escapes from loops and switch control structures. C would be improved if it had better escape constructs. (b) C has no provision for epilogs -- an epilog being a block of code executed after an escape. I don't quite see how one puts epilogs in C in any natural fashion. The question I was raising was whether the rare instances where a goto is actually useful warrant retaining it in the language. You and I, of course, will use them wisely and only when warranted. But I could do without them in C. The times that I would have to get around not having them available are few compared to other awkwardnesses in the language. Incidentally, the objection I have to older Fortran is not so much the abusive use of goto's (a complaint I will leave to theoreticians) but is their awkwardness -- expressing any kind of complicated logic in Fortran 66 is rather like solving a chinese box puzzle. -- In the fields of Hell where the grass grows high Are the graves of dreams allowed to die. Richard Harter, SMDS Inc.
decot@hpisod2.HP.COM (Dave Decot) (03/07/88)
> The following example is illustrative syntax only: > > [int *,int] foobar(); > .... > [ptr,flag] = foobar(arg) > int arg; > { > int *a, *b; > .... > return [a,1]; > .... > return [b,0]; > } How about this syntax: struct foo { int *x, y; } foobar(); struct foo foobar(arg) int arg; { int *a, *b; ... return {a, 1}; ... return {b, 0}; } This way, all you're adding to the existing language is aggregate expressions. Dave Decot hpda!decot
gregg@a.cs.okstate.edu (Gregg Wonderly) (03/08/88)
From article <25200@cca.CCA.COM>, by g-rh@cca.CCA.COM (Richard Harter): > In article <2403@umd5.umd.edu> chris@trantor.umd.edu (Chris Torek) writes: >>-In article <24996@cca.CCA.COM> g-rh@cca.CCA.COM (Richard Harter) writes: >>-}As a side point, I like the suggestion that someone made that there be >>-}a fallthrough statement rather than automatic fallthrough. > >>While this would work, if we assume D has aggregate constructors, >>there is a handsomer way: > >> switch (e) { >> case [1, 2, 3, 5, 7, 13, 19, 23]: ... >> } >> /* syntax above is only for demonstration */ > > Well, yes, one ought to be able to do that. However it is isn't quite > as strong as fallthrough, where one can say > > switch (e) { > case foo: > some code; > fallthrough; > case baz: > some more code; > } > > > In C as it stands now you can do this -- indeed, the complaint is that > one can do this unintentionally. If one adds aggregate constructors and > takes away automatic fallthrough, it seems to me that you weaken the > language. No doubt there are purists that say you shouldn't do the > sort of thing given above. I wouldn't go that far, but I would agree > that one should be able to use aggregate constructors when cases actually > share code. This is just one of the many things that people gripe and complain about when writing C. In my eyes it comes down to poor/bad (which is your favorite?) program writing practices. You can eliminate the majority of the problems associated with `break' in a switch by prototyping the entire switch before filling in the code (you mean you are coding off the top of your head :-). e.g. switch (val) { case 'a': case 'b': break; case 'c': break; case 'f': case 'h': case 'v': break; default: wrong_again_honey(); } Then you can add the code for each case by opening a line above the `break' statements. If you know that you need a switch, you should know what the structure of it will be initially (prior to adding that other feature), so just type it (you know, like stubbing out a program). While I am at it, one of my biggest gripes is that too many people exploit C's idea of a statement so that they can write if (some_expression) for (init; continuation_condition; repeat_code) switch (val) { forty jillion case's } without placing braces around the `switch' and the `for'. I guess these people either don't use VI, or haven't got a bracket matcher in their favorite editor. This kind of code is terribly difficult to get through. I prefer... if (some_expression) { for (init; continuation_condition; repeat_code) { switch (val) { forty jillion case's } } } Then there are those people who lost sight of their space bar and insist on typing things like, for(a=4,b=5,s=&c;((a<b&&d>>4)+(int)s+1)!=c<d;++a,--d,c=a) or some such arcane expression without a single space to try and make the code even slightly more readable. Well, I will stop here, as programming style is kind of like editors and religion. Everybody has there own, and nobody can make them change... sigh!!! ----- Gregg Wonderly Department of Computing and Information Sciences Oklahoma State University UUCP: {cbosgd, ihnp4, rutgers}!okstate!gregg Internet: gregg@A.CS.OKSTATE.EDU
karl@haddock.ISC.COM (Karl Heuer) (03/09/88)
In article <562@acf3.NYU.EDU> pedersen@acf3.UUCP (paul pedersen) writes: >Fallthrough may be occasionally justifiable. [As in case Subtract doing a >negate and then falling into case Add.] Of course, even C-like fallthrough isn't good enough if you have THREE cases that end up at the same point. (Assuming the code to be executed is AC | BC | C, not ABC | BC | C.) Or even two cases with different initial segments (AC | BC). Might just as well use the same construct (goto) for all of these. (But I do like `goto case X` and `goto default`; one ought to be able to use the existing label.) >I do not think, however, that such a "general" concept as either fallthrough >or on-the-fly constructors should be used to support the common use of >multiple cases which execute exactly the same code. Here some other syntax >is needed. I agree. >The natural 'case a,b,c:' is defeated by C's comma operator, That's no worse than the other comma conflicts. If the comma operator is still illegal in constant expressions (it was in an early Draft; I haven't checked the latest one), this syntax could be added to C with no conflicts. If not, then could add it anyway, and let it override the comma operator just as `f(x,y)` does. (If for any reason you actually need a comma operator in a case statement, you write `case (A,B,C):`.) If we're talking D rather than C, we don't even have to worry about breaking existing code. Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
jbn@glacier.STANFORD.EDU (John B. Nagle) (03/09/88)
Provided that goto operations are limited to forward and outward transfers of control, they are relatively harmless, although they do make flow analysis within an optimizing compiler more difficult.
g-rh@cca.CCA.COM (Richard Harter) (03/09/88)
> >Of course, even C-like fallthrough isn't good enough if you have THREE cases >that end up at the same point. (Assuming the code to be executed is AC | BC | >C, not ABC | BC | C.) Or even two cases with different initial segments (AC | >BC). Might just as well use the same construct (goto) for all of these. (But >I do like `goto case X` and `goto default`; one ought to be able to use the >existing label.) A more general solution runs as follows: Associate a flag with each such block.Set the execution control flags in the switch case blocks; execute the blocks under if control. E.g. ex_A = ex_B = ex_C = 0; switch (foo) { case 0: ex_A = ex_C = 1; break; case 1: ex_B = ex_C = 1; break; case 2: ex_C = 1; break; default: break; } if (ex_A) {A} if (ex_B) {B} if (ex_C) {C} This works quite nicely if the blocks are to be sequentially executed. In the general case more than one block may be executed per case, in varying orders, and the order is affected by results within blocks, I would much rather use state machine logic and transition tables than a rats nest of goto's. This is a personal taste, no doubt, but I find it is easier to construct and maintain the tables, then it is to go into the code and fiddle with the logic. -- In the fields of Hell where the grass grows high Are the graves of dreams allowed to die. Richard Harter, SMDS Inc.
terry@wsccs.UUCP (terry) (03/20/88)
In article <24996@cca.CCA.COM> Richard Harter writes: As a side point, I like the suggestion that someone made that there be a fallthrough statement rather than automatic fallthrough. In a later article, Richard gives an example of his proposed syntactic change, in response to suggestion that aggregate constructors would not be as strong as the current language: From article <25200@cca.CCA.COM>, Richard Harter: Well, yes, one ought to be able to do that. However it is isn't quite as strong as fallthrough, where one can say switch (e) { case foo: some code; fallthrough; case baz: some more code; } I think that this would be dangerous, in that it would have further reaching effects than Richard anticipates. Since the discussion centers around 'D', which by context is simply "a new standard C which may not be compatable with the current K&R standard of C so we'll call it 'D'", I believe that "upward" compatability must be maintained if all C software is to be recompiled in D with minimal tweaking. While a change this drastic _could_ be worked around with something as simple as #define case fallthrough; case this does _not_ help code generators such as YACC and LEX generate this new code. There are quite a number of developement tools which would have to either be sacraficed or rewritten if D is to become the new standard. While this will necessary if _any_ changes are made from the current C definition, these changes should, as a matter of course, be minimized. The alternative is a new language which will require ALL new tools, something I think a great deal of people would rather avoid. Chris' example of aggregate constructors was: In article <2403@umd5.umd.edu> Chris Torek: While this would work, if we assume D has aggregate constructors, there is a handsomer way: switch (e) { case [1, 2, 3, 5, 7, 13, 19, 23]: ... } /* syntax above is only for demonstration */ Given that the syntax is only for demonstration, I would suggest the following as a better (more backward compatable) method: switch( e) { case 1,2,3,4: } While apparently at conflict with the comma operator, one should realize that the compiler expects a constant, not an expression, following a case statement, so there would be no ambiguity in parsing. In article <3278@okstate.UUCP>, Gregg Wonderly has suggested that the whole complaint centers on poor programming practices: You can eliminate the majority of the problems associated with `break' in a switch by prototyping the entire switch before filling in the code (you mean you are coding off the top of your head :-). I don't think this is entirely true. If you are a paper-programmer (one that writes code on paper, and when it's "right", types it in), this is probably what you'd think. A great deal of programmers (myself included) DO program off the tops of our heads, when the job is sufficiently simple. In addition, when it isn't, I use a tool called an editor ;-). With it, I can move case statements around to the point that I break them :-(. This type of breakage would not be helped by prototyping, but WOULD be caught if 'fallthrough' were mandatory, my workaround withstanding. A 'fallthrough' statement would save a careless programmer (Yech! 11 characters!), but I still think it'll make "upgrading" previous code *much* more difficult. Besides, I never make that kind of mistake ;-). Gregg also complains of a style issue, particularly, leaving the braces off of the 'if' and 'for' in the following example: if (some_expression) for (init; continuation_condition; repeat_code) switch (val) { forty jillion case's } complaining that it is hard to get through and find the end of the statement. Why not use the '{' following the switch? Indentation is part of style, too. | Terry Lambert UUCP: ...{ decvax, ihnp4 } | | @ Century Software : ...utah-cs!uplherc!sp7040!obie!wsccs!terry | | SLC, Utah | | These opinions are not my companies, but if you find them | | useful, send a $20.00 donation to Brisbane Australia... |
chris@mimsy.UUCP (Chris Torek) (03/23/88)
In article <336@wsccs.UUCP> terry@wsccs.UUCP (terry) writes: >... Since the discussion centers around 'D', which by context is >simply "a new standard C which may not be compatable with >the current K&R standard of C so we'll call it 'D'", That is not what *I* mean by `D'. I thought we were discussing a language that would be a replacement for C as a systems and/or general-purpose programming language. >I believe that "upward" compatability must be maintained if all C >software is to be recompiled in D with minimal tweaking. I had in mind more of an automatic translator (in which inserting `fallthrough' statements would be trivial). Of course this is largely just an intellectual exercise anyway (and, some say, a light workout at that :-) ). >While a change this drastic [requiring `fallthrough' for case fallthrough] >_could_ be worked around with something as simple as > > #define case fallthrough; case > >this does _not_ help code generators such as YACC and LEX generate this new >code. Actually, as it happens, both yacc and lex always generate the `break' for you. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
karl@haddock.ISC.COM (Karl Heuer) (03/23/88)
In article <336@wsccs.UUCP> terry@wsccs.UUCP (terry) writes: >I think that [an explicit fallthrough statement] would be dangerous, in that >it would have further reaching effects than Richard anticipates. Since the >discussion centers around 'D', which by context is simply "a new standard C >which may not be compatable with the current K&R standard of C so we'll call >it 'D'", I believe that "upward" compatability must be maintained if all C >software is to be recompiled in D with minimal tweaking. That sounds more like a description of ANSI C. "D" is intended to be a language which is as powerful as C but (unlike ANSI C and C++) is *not bound at all* by the compatibility requirement. (Although a C-to-D translation tool would probably appear, in the unlikely event that this thought experiment actually materializes into something tangible.) >While a change this drastic _could_ be worked around with something as simple >as #define case fallthrough; case this does _not_ help code generators >such as YACC and LEX generate this new code. What's the problem? I think yacc currently generates a break after each case block; the solution then is to omit this if generating D. Even if yacc does depend on the current automatic fall-through semantics, it could generate an explicit "fallthrough;" statement at the bottom of each case block. Surely it has enough information for this -- I'd bet it's a one-line change. (Btw, although I would prefer a "fallthrough" statement to the current C semantics, I think that both are inferior to "goto case N".) Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
rcvie@tuvie (ELIN Forsch.z.) (03/23/88)
In article <336@wsccs.UUCP< terry@wsccs.UUCP (terry) writes:
<Given that the syntax is only for demonstration, I would suggest the following
<as a better (more backward compatable) method:
<
< switch( e) {
< case 1,2,3,4:
< }
<
<While apparently at conflict with the comma operator, one should realize that
<the compiler expects a constant, not an expression, following a case statement,
<so there would be no ambiguity in parsing.
<
Just spell it correct. The compiler expects neither a `constant' nor an
`expression' but a `constant expression'. As, however, the comma operator
must not be part of a constant expression (as far as my dpANS-edition says)
there still is no ambiguity. But this restriction is still discussed.
Dietmar Weickert,
ALCATEL-ELIN Research Center, Vienna, Austria.