Topic: Proposal - enhancement for switch statement.
Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Thu, 19 Nov 1992 16:39:44 GMT Raw View
In article <1992Nov16.213015.1@vax1.bham.ac.uk> mccauleyba@vax1.bham.ac.uk (Brian McCauley) writes:
>
>Now (as promised) why ranges would be unnecessary (following the "if you
>can do it with a class then don't extend the language" rule ;-) )
>
>template <class T> class Range {
> T top,bottom;
>public:
> Range(t,b) : top(t),bottom(b) {}
> int operator==(T t) { return bottom <= t && top >= t; }
>}
>template <class T> inline operator == (T& t,Range<T>& r) {return r == t;}
>
>switch (i) {
> case Range<char>('0','9') : case '-' : {/*...*/} break;
>}
This is illegal.
The 'argument' of a case must be a constant expression.
switch is a primitive operation, the compiler must be able to
check for duplicate cases.
>
>compare:
>
>switchc (i) {
> case '0'...'9' , '-': /*...*/
>}
Much nicer :-)
>
>OK so it's not as concise as your form but it far more C++. Assuming your
>compiler supports inlining it can be made to optimize as well as the
>explicit range syntax.
>
>BTW heres a new constuct (my mother's favorate in BASIC) that would become
>available:
>
>switch(TRUE) {
> case expr1: { /* code */} break;
> case expr2: { /* code */} break;
> delfault: {/* code */}
>}
>
>--
> \\ ( ) No Bullshit! | Email: B.A.McCauley@bham.ac.uk
> . _\\__[oo from | Voice: +44 21 471 3789 (home)
>.__/ \\ /\@ /~) /~[ /\/[ | Fax: +44 21 625 2175 (work)
>. l___\\ /~~) /~~[ / [ | Snail: 197 Harborne Lane,
> # ll l\\ ~~~~ ~ ~ ~ ~ | Birmingham, B29 6SS, UK
>###LL LL\\ (Brian McCauley) | ICBM: 52.5N 1.9W
--
;----------------------------------------------------------------------
JOHN (MAX) SKALLER, maxtal@extro.ucc.su.oz.au
Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------
Author: pete@cssc-syd.tansu.com.au (Peter Alexander Merel)
Date: Thu, 19 Nov 1992 21:32:23 GMT Raw View
mccauleyba@vax1.bham.ac.uk (Brian McCauley) writes:
>No. Please not another token in the parser.
Agreed. The C++ philosophy appears to be, only add a token if there's just
no other way at all.
>The only problem is that if a class has both an `operator==(int)' and an
>`operator int ()' defined inconsistantly the some existing switch
>statements would change their meaning.
I thought about this a little, and thanks to Mike Tiller for raising it
privately. Mike raised an associated problem, that operator== doesn't
have to return an int, but can return any damned thing it pleases ...
so a really perverse operator== might return something that can't cast
into an int.
But how about this:
switch looks, in the first instance, for a way to cast the switch
expression and case constants into int. That should keep existing code
from breaking.
Failing to find a way to do this, switch looks for an operator== on the
type returned from the switch expression with a distinctive signature, say
int const operator==(foo const & bar) const
There's a precedent for this sort of usage in the copy constructor, and
of course this way the case constants can have different types to match
different overloaded operator==.
>switch(TRUE) {
> case expr1: { /* code */} break;
> case expr2: { /* code */} break;
> delfault: {/* code */}
>}
>This is exactly equivalent to an if ladder but some people think it looks
>nicer :-)
Looks way cool, but I think there's a little inconsistency with the previous
switch - you've got the constant at the top, and we'd have to commute the
operator== to get it to fly. Sounds messy to me.
--
Internet: pete@cssc-syd.tansu.com.au UUCP: {uunet,mcvax}!munnari!cssc-syd!pete
Snail: 1/18-20 Orion Road, Lane Cove NSW 2066 Australia Phone: +61 2 911 3130
I am a big pond in a small fish.
Author: mccauleyba@vax1.bham.ac.uk (Brian McCauley)
Date: Fri, 20 Nov 1992 19:50:59 GMT Raw View
In article <1992Nov19.163944.19935@ucc.su.OZ.AU>, maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) writes:
> In article <1992Nov16.213015.1@vax1.bham.ac.uk> mccauleyba@vax1.bham.ac.uk (Brian McCauley) writes:
>>Now (as promised) why ranges would be unnecessary (following the "if you
>>can do it with a class then don't extend the language" rule ;-) )
>>
>>template <class T> class Range {
>> T top,bottom;
>>public:
>> Range(t,b) : top(t),bottom(b) {}
>> int operator==(T t) { return bottom <= t && top >= t; }
>>}
>>template <class T> inline operator == (T& t,Range<T>& r) {return r == t;}
>>
>>switch (i) {
>> case Range<char>('0','9') : case '-' : {/*...*/} break;
>>}
>
> This is illegal.
> The 'argument' of a case must be a constant expression.
> switch is a primitive operation, the compiler must be able to
> check for duplicate cases.
>
Of course it's illegal! I do wish people would pay attension to the
subject of the thread. What is being to discussed is the merit of
allowing the switch command to accept an argument of any type and
resolve cases using the appropriate == operator. Since there's no
such thing as a compile-time class object constant this implies that
the case clause would have to be able to take an expression if
this proposal were accepted. It would also mean that switch was not
a primative operation (unless it happened to obey C rules in which
case it would optimize back to a primative operation). The ability for
the compiler to detect duplicate cases would also be lost when varaible
expressions where use in case clauses (or when the == operator was not
inline). The complier does not _need_ to be able to detect duplicate
cases if its not trying to make a jump table and it couldn't in these
cases anyhow.
The range issue was introduced as an amendment and I was demonstrating
that it was redundant given the implications of the original proposal.
IMHO...
On balance the extension of switch to operate on any class is probably
unecessary and the extension to case to take ranges is therefore a
good idea.
Removing the need for break between case clauses is a bad idea
- the need to avoid fallthrough is better handled by a complier warning.
Adding a new syntax (or worse still token) to the language should not
be done lightly.
--
\\ ( ) No Bullshit! | Email: B.A.McCauley@bham.ac.uk
. _\\__[oo from | Voice: +44 21 471 3789 (home)
.__/ \\ /\@ /~) /~[ /\/[ | Fax: +44 21 625 2175 (work)
. l___\\ /~~) /~~[ / [ | Snail: 197 Harborne Lane,
# ll l\\ ~~~~ ~ ~ ~ ~ | Birmingham, B29 6SS, UK
###LL LL\\ (Brian McCauley) | ICBM: 52.5N 1.9W
Author: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Mon, 23 Nov 1992 00:14:18 GMT Raw View
mccauleyba@vax1.bham.ac.uk (Brian McCauley) writes:
>Removing the need for break between case clauses is a bad idea -
>the need to avoid fallthrough is better handled by a compiler warning.
The difficulty with this is that there has to be a standard way to
prevent the compiler warning in cases where the fallthrough is
deliberate rather than accidental. Because there isn't, the compiler
warning would wrong more times than not, and so most compilers don't
seem to include such a warning.
Perhaps a good way to indicate that the fallthrough is deliberate would be to
use an explicit goto.
switch(x) {
case 1:
...
break;
case 2:
...
goto case_3: /* fall through */
case_3:
case 3:
...
}
Any half-decent compiler will of course be able to optimize out the goto.
It is still irritating that you are not allowed to jump to case labels,
so you have to invent an extra case_3 label. [ObLanguageExtension:
allow goto's to jump to case labels (so long as the jump is unambiguous)].
//----------------------------------------------------------------------//
A technique that I have seen used is to
#define when break; case
#define otherwise break; default
and then use
switch(x) {
when 1:
...
when 2:
...
otherwise:
...
}
If your compiler doesn't warn about fallthrough in switch statements,
then this may well be worthwhile.
--
Fergus Henderson fjh@munta.cs.mu.OZ.AU
This .signature virus is a self-referential statement that is true - but
you will only be able to consistently believe it if you copy it to your own
.signature file!
Author: gjditchf@plg.uwaterloo.ca (Glen Ditchfield)
Date: Mon, 23 Nov 1992 14:40:18 GMT Raw View
In article <9232811.8739@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
>Perhaps a good way to indicate that the fallthrough is deliberate would be to
>use an explicit goto.
> switch(x) { ...
> case 2: ...
> goto case_3: /* fall through */
> case_3: /* [Note the "_"!] */
> case 3: ...
> }
>
>Any half-decent compiler will of course be able to optimize out the goto.
>It is still irritating that you are not allowed to jump to case labels,
If I remember correctly, the Plan 9 C compiler does allow "goto case 3"
(that is, with no underscore).
Author: pete@cssc-syd.tansu.com.au (Peter Alexander Merel)
Date: Tue, 24 Nov 1992 02:46:00 GMT Raw View
mccauleyba@vax1.bham.ac.uk (Brian McCauley) writes:
>In article <1992Nov19.163944.19935@ucc.su.OZ.AU>, maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) writes:
>[It's illegal]
>Of course it's illegal!
>What is being discussed is the merit of allowing the switch command to
>accept an argument of any type and resolve cases using the appropriate ==
>operator.
Or any extension that achieves the same end - to allow switch to deal with
types that don't go onto int.
>no such thing as a compile-time class object constant this implies that
>the case clause would have to be able to take an expression if
>this proposal were accepted.
Yes.
>It would also mean that switch was not
>a primitive operation (unless it happened to obey C rules in which
>case it would optimize back to a primative operation).
So far no one's suggested a reason this could not be made so.
>The ability for
>the compiler to detect duplicate cases would also be lost when varaible
>expressions where use in case clauses (or when the == operator was not
>inline).
The compiler can still check for duplicate cases when switch devolves to
the simple C variety, and of course this won't break code (it is more
permissive than otherwise).
>The range issue was introduced as an amendment and I was demonstrating
>that it was redundant given the implications of the original proposal.
Yah.
>IMHO...
>On balance the extension of switch to operate on any class is probably
>unecessary and the extension to case to take ranges is therefore a good idea.
I'd rather have a simple semantic extension that does the job for a broad
range of expressions than a syntactic hack that doesn't do much of the
job at all. Ranges won't, for a good instance, allow use of switch with
string classes.
>Adding a new syntax (or worse still token) to the language should not
>be done lightly.
You ain't just whistling dixie.
--
Internet: pete@cssc-syd.tansu.com.au UUCP: {uunet,mcvax}!munnari!cssc-syd!pete
Snail: 1/18-20 Orion Road, Lane Cove NSW 2066 Australia Phone: +61 2 911 3130
I am a big pond in a small fish.
Author: dfoster@jarthur.claremont.edu (Derek R. Foster)
Date: Mon, 16 Nov 1992 00:45:58 GMT Raw View
In article <MCGRANT.92Nov15132747@rascals.stanford.edu> mcgrant@rascals.stanford.edu (Michael C. Grant) writes:
>In article <1992Nov15.190830.11622@cssc-syd.tansu.com.au>
>pete@cssc-syd.tansu.com.au (Peter Alexander Merel) writes:
> Before I describe what occurred to me, yes folks I know that a switch is not
> as useful as a virtual function.
> But what I'd like for a snippet I'm working on at the moment would be for
> switch to use operator== rather than relying on conversion to int, so that
> I could use it to test cases on objects rather than building an industrial
> strength if statement.
...
> Any chance?
>
>I doubt it... Even before C++ came along, switch statements could not
>handle floats, strings, structures, etc. Sure, a switch statement could
>be turned into a set of if-then-else statements, but it isn't because
>it usually is best turned into a jump table.
Personally, I would like to see a few more changes made to the 'switch'
statement (in my opinion, one of the ugliest warts on the face of the
C language, and regrettably C++ also.)
I wouldn't advise actually getting rid of the 'switch' statement (it is
needed for backwards compatibility), but instead creating a (somewhat)
parallel statement, called, say, 'switchc'. The purposes of 'switchc' would be:
1) Remove the requirement for a 'break' after each statement (the accidental
omission of which is the cause of a ridiculously large number of errors
within C programs.) Make a 'break' the default, rather than fallthrough
being the default. Add a 'fallthrough' keyword (or extend the
meaning of the 'continue' keyword perhaps?) to still allow programmers
to use fallthrough in those rare occasions for which it is necessary/
desirable.
2) Allow multiple labels for each 'case' statement, and extend the case
statement to allow ranges and multiple arguments (as in Pascal.) This
avoids the excessively verbose lists of 'case' statements that are
necessary in C to represent ranges of values.
as per Peter's suggestion, we could easily add...
3) for class-typed switching variables, we could require that 'switchc'
use operator == and a simple linear search to work its way down the
list. (yes, this is possibly inefficient. It is, however, extremely
clear code which is easy to read.) In the case of non-class-typed
switching variables, the compiler is allowed to use a jump table,
binary search, or whatever seems appropriate.
4) in implementing 3, we could allow, for class typed variables, the
switch labels to be any data type for which operator == with the
given class type is defined. For instance, char *'s, floating point
values, (references to const objects?,) etc.
thus, code like:
int x;
switch(x)
{
case '1': case '2': case '3': case '4': case '5': case '6':
case '7': case '8': case '9': case '0':
cout << "x is a digit\n";
case 'A':
cout << "x is a digit or the letter 'A'\n";
break;
default:
cout << "x is not a digit or the letter 'A'\n";
}
could also be written:
switchc(x)
{
case 0...9: cout << "x is a digit\n"; fallthrough;
case 'A': cout << "x is a digit or the letter 'A'\n;
default: cout << "x is not a digit or the letter 'A'\n";
}
also, consider the possibility of code like:
String command;
cin >> command;
switchc(command)
{
case "DIR": do_directory();
case "VIEW": do_view();
case "QUIT","EXIT": do_exit();
default: cerr << "Unknown command!";
}
If the above code is written using 'if's, it is a) much harder to
read, b) much harder to understand (the reader may not immediately
see the overall structure), and c) more error-prone (it is possible to
get mismatched 'else's, etc. which will compile fine, but not do
what they're supposed to.
I would LOVE to see the addition of something like this to the language.
Control flow in many of my programs would be vastly simplified, and I
think the number of errors I would make would be substantially smaller.
>I wouldn't object to such an addition in principle, except for the fact
>that it's unnecessary (a big objection for sure).
C++ is unnecessary. It's there because it makes programs easier to understand,
and because it reduces programming errors. Those seem like good reasons to me.
I think this extension satisfies those goals as well.
>Michael C. Ggrant
Derek Riippa Foster
Author: mccauleyba@vax1.bham.ac.uk (Brian McCauley)
Date: Mon, 16 Nov 1992 21:30:15 GMT Raw View
In article <1992Nov16.004558.9855@muddcs.claremont.edu>, dfoster@jarthur.claremont.edu (Derek R. Foster) writes:
> In article <MCGRANT.92Nov15132747@rascals.stanford.edu> mcgrant@rascals.stanford.edu (Michael C. Grant) writes:
>>In article <1992Nov15.190830.11622@cssc-syd.tansu.com.au>
>>pete@cssc-syd.tansu.com.au (Peter Alexander Merel) writes:
>> ...
>> switch to use operator== rather than relying on conversion to int, so that
>> I could use it to test cases on objects rather than building an industrial
>> strength if statement.
> ...
> Personally, I would like to see a few more changes made to the 'switch'
> statement (in my opinion, one of the ugliest warts on the face of the
> C language, and regrettably C++ also.)
>
> I wouldn't advise actually getting rid of the 'switch' statement (it is
> needed for backwards compatibility), but instead creating a (somewhat)
> parallel statement, called, say, 'switchc'.
^^^^^^^
No. Please not another token in the parser.
>
> 1) Remove the requirement for a 'break' after each statement (the accidental
> omission of which is the cause of a ridiculously large number of errors
> within C programs.) Make a 'break' the default, rather than fallthrough
> being the default. Add a 'fallthrough' keyword (or extend the
> meaning of the 'continue' keyword perhaps?) to still allow programmers
> to use fallthrough in those rare occasions for which it is necessary/
> desirable.
I agree the default behavior is wrong but were all used to it so lets keep it.
All that is needed is to _advise_ compiler implementors that they should
give a warning if `case' (or `default') is preceded by anything other than
`break;' or `case:' (except of course the first one). Good C++ programmers
treat warnings as errors unless they are compling C sources.
While were about it perhaps we should point out that compliers should warn
when variables defined in one case are used in another (IMHO each case
should always be given it's own scope).
>
> 2) Allow multiple labels for each 'case' statement, and extend the case
> statement to allow ranges and multiple arguments (as in Pascal.) This
> avoids the excessively verbose lists of 'case' statements that are
> necessary in C to represent ranges of values.
Multiple arguments - agian I'd agree if we were inventing a language from
scratch but I'm not sure there's any need to change the syntax for
a saving of 3 characters. Likewise for ranges would (see below).
>
> as per Peter's suggestion, we could easily add...
>
> 3) for class-typed switching variables, we could require that 'switchc'
> use operator == and a simple linear search to work its way down the
> list. (yes, this is possibly inefficient. It is, however, extremely
> clear code which is easy to read.) In the case of non-class-typed
> switching variables, the compiler is allowed to use a jump table,
> binary search, or whatever seems appropriate.
> 4) in implementing 3, we could allow, for class typed variables, the
> switch labels to be any data type for which operator == with the
> given class type is defined. For instance, char *'s, floating point
> values, (references to const objects?,) etc.
...
> ...using 'if's, it is a) much harder to
> read, b) much harder to understand (the reader may not immediately
> see the overall structure), and c) more error-prone (it is possible to
> get mismatched 'else's, etc. which will compile fine, but not do
> what they're supposed to.
>
> I would LOVE to see the addition of something like this to the language.
> Control flow in many of my programs would be vastly simplified, and I
> think the number of errors I would make would be substantially smaller.
Yes I agree but let's non pollute _everyones_ namspace by adding a new token.
The only problem is that if a class has both an `operator==(int)' and an
`operator int ()' defined inconsistantly the some existing switch statements
would change their meaning.
Now (as promised) why ranges would be unnecessary (following the "if you
can do it with a class then don't extend the language" rule ;-) )
template <class T> class Range {
T top,bottom;
public:
Range(t,b) : top(t),bottom(b) {}
int operator==(T t) { return bottom <= t && top >= t; }
}
template <class T> inline operator == (T& t,Range<T>& r) {return r == t;}
switch (i) {
case Range<char>('0','9') : case '-' : {/*...*/} break;
}
compare:
switchc (i) {
case '0'...'9' , '-': /*...*/
}
OK so it's not as concise as your form but it far more C++. Assuming your
compiler supports inlining it can be made to optimize as well as the
explicit range syntax.
BTW heres a new constuct (my mother's favorate in BASIC) that would become
available:
switch(TRUE) {
case expr1: { /* code */} break;
case expr2: { /* code */} break;
delfault: {/* code */}
}
This is exactly equivalent to an if ladder but some people think it looks
nicer :-)
--
\\ ( ) No Bullshit! | Email: B.A.McCauley@bham.ac.uk
. _\\__[oo from | Voice: +44 21 471 3789 (home)
.__/ \\ /\@ /~) /~[ /\/[ | Fax: +44 21 625 2175 (work)
. l___\\ /~~) /~~[ / [ | Snail: 197 Harborne Lane,
# ll l\\ ~~~~ ~ ~ ~ ~ | Birmingham, B29 6SS, UK
###LL LL\\ (Brian McCauley) | ICBM: 52.5N 1.9W
Author: pl2440@meibm31.cen.uiuc.edu (Paul Jay Lucas)
Date: Mon, 16 Nov 1992 23:51:07 GMT Raw View
In <1992Nov16.213015.1@vax1.bham.ac.uk> mccauleyba@vax1.bham.ac.uk (Brian McCauley) writes:
>Now (as promised) why ranges would be unnecessary (following the "if you
>can do it with a class then don't extend the language" rule ;-) )
>template <class T> class Range {
> T top,bottom;
>public:
> Range(t,b) : top(t),bottom(b) {}
> int operator==(T t) { return bottom <= t && top >= t; }
>}
>template <class T> inline operator == (T& t,Range<T>& r) {return r == t;}
>switch (i) {
> case Range<char>('0','9') : case '-' : {/*...*/} break;
>}
*****> Both cfront 3.0.1 and I think the above is illegal.
--
- Paul J. Lucas University of Illinois
AT&T Bell Laboratories at Urbana-Champaign
Naperville, IL pjl@cs.uiuc.edu
Author: mat@mole-end.matawan.nj.us
Date: Tue, 17 Nov 1992 06:38:45 GMT Raw View
In article <1992Nov16.004558.9855@muddcs.claremont.edu>, dfoster@jarthur.claremont.edu (Derek R. Foster) writes:
> In article <MCGRANT.92Nov15132747@rascals.stanford.edu> mcgrant@rascals.stanford.edu (Michael C. Grant) writes:
> >In article <1992Nov15.190830.11622@cssc-syd.tansu.com.au>
> >pete@cssc-syd.tansu.com.au (Peter Alexander Merel) writes:
> 1) ... Make a 'break' the default, rather than fallthrough
> being the default. Add a 'fallthrough' keyword (or extend the
> meaning of the 'continue' keyword perhaps?) ...
Please don't suggest ~break . I can imagine the (ahem) consternation
in x3j16/WG21 at another ~-punned keyword.
--
(This man's opinions are his own.)
From mole-end Mark Terribile
mat@mole-end.matawan.nj.us, Somewhere in Matawan, NJ