The C++ Standard (ISO/IEC 14882, Second edition, 2003-10-15)
says under 6.4.2(2) [see also 5.19]:
case constant-expression :
I propose that the case expression of the switch statement
be changed from "integral constant-expression" to "integral expression".
This opens up many new possibilities since then also function calls
would be permitted in the case expression.
The old case case would continue to function since
it is a subset of the new case case.
Example usage:
//...
int f()
{
//...
return BLA1;
}
int g()
{
//...
return BLA2;
}
int h()
{
//...
return BLA3;
}
int y, x = f();
switch (x)
{
case 123 : y = g(); break;
case g() : y = 456; break; // using new case feature, ie. func-call
case h() : y = 789; break; // ditto
default : y = -1; break;
}
> C/C++ language proposal:
> Change the 'case expression' from "integral constant-expression" to
> "integral expression"
>
> The C++ Standard (ISO/IEC 14882, Second edition, 2003-10-15)
> says under 6.4.2(2) [see also 5.19]:
[snip]
Sorry, but your proposal is off-topic for the comp.lang.c newsgroup. I
suspect that it is also off-topic for comp.lang.c++.
As C and C++ are two separate languages, each defined by their own
standards, I suggest that you break your "C/C++" proposal into a "C
proposal" and a separate "C++ proposal", and submit each to their
respective standards bodies.
For C, you /might/ post your proposal to the comp.std.c newsgroup, as that
is the group that discusses the ramifications of and enhancements to the C
language standard.
--
Lew Pitcher
Master Codewright & JOAT-in-training | Registered Linux User #112576
http://pitcher.digitalfreehold.ca/ | GPG public key available by request
---------- Slackware - Because I know what I'm doing. ------
switch (rand()) {
case rand(): puts ("What's"); break;
case rand(): puts ("wrong"); break;
case rand(): puts ("with"); break;
case rand(): puts ("this"); break;
case rand(): puts ("picture?"); break;
}
Well, it *could* be defined to be equivalent to the following:
{
const int __tmp = rand();
if (__tmp == rand() puts("What's");
else if (__tmp == rand() puts("wrong");
else if (__tmp == rand() puts("with");
else if (__tmp == rand() puts("this");
else if (__tmp == rand() puts("picture?");
}
That's intended to be equivalent code just for this example, not a
definition; other transformations would be required when one case
falls through to the next. And I refuse to think about how this would
affect Duff's Device.
The point of C's switch statement is that it can be implemented
efficiently as a jump table, in contrast to an if/else chain where the
conditions have to be evaluated sequentially.
If you want to do something like a switch statement, but where the
case values aren't constant, you can always just write an if/else
chain. If you want to support this kind of thing in the language, so
that you only have to specify the LHS of the comparison once, I think
I'd prefer to introduce a new construct rather than adding this
functionality to the existing switch/case construct. With this
proposal, changing a single expression from a constant to a
non-constant could have substantial effects on the generated code, and
depending on how it's defined, subtle effects on the code's behavior.
--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
In addition, it is a BAD IDEA. Eliminating the constant provision
prevents making efficient transfer tables. It even prevents
discovering that such tables are feasible (or not).
--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
> Lew Pitcher wrote:
> > Adem (for-us...@alicewho.com) wrote:
> >
> >> C/C++ language proposal:
> >> Change the 'case expression' from "integral constant-expression"
> >> to "integral expression"
[...]
> In addition, it is a BAD IDEA. Eliminating the constant provision
> prevents making efficient transfer tables. It even prevents
> discovering that such tables are feasible (or not).
It doesn't eliminate the privision, it just makes it more difficult. In
effect, optimizing
switch ( f() )
{
case 1: { /* A */ } break;
case 2: { /* B */ } break;
case 3: { /* C */ } break;
case 4: { /* D */ } break;
}
becomes similar optimizing
int const x = f();
if ( x == 1 ) { /* A */ }
else if ( x == 2 ) { /* B */ }
else if ( x == 3 ) { /* C */ }
else if ( x == 4 ) { /* D */ }
since both involve an unchanging value and constant values being compared
to. A compiler could convert both into a jump table or binary search.
Wonderful!
>
> Well, it *could* be defined to be equivalent to the following:
>
> {
> const int __tmp = rand();
> if (__tmp == rand() puts("What's");
> else if (__tmp == rand() puts("wrong");
> else if (__tmp == rand() puts("with");
> else if (__tmp == rand() puts("this");
> else if (__tmp == rand() puts("picture?");
>
> }
>
If rand() generates equal random numbers frequently, is it meaningful?
That code neither makes nor implies any assumptions about how frequently
rand() generates random numbers.
What's wrong with it that you're just injecting garbage. You can use the
same rand() trick with any statement:
for (i=rand(); i<=rand(); i=rand())...
But there's nothing really wrong with the OP's proposal, except it should be
a new statement type to keep the switch semantics clean. Then, it could also
use non-integer types.
C likes it's rather austere syntax however so I doubt this change will ever
be made.
--
Bartc
There is something wrong. Try to compile this example, and I hope you
figure whats wrong with his proposal :
int main()
{
int a = 3;
switch( a )
{
case 3:
a = 1;
break;
case 3:
a = 2;
break;
case 3:
a = 3;
break;
default:
a = 4;
}
}
>> But there's nothing really wrong with the OP's proposal, except it should
>> be a new statement type to keep the switch semantics clean. Then, it
>> could also use non-integer types.
>
> There is something wrong. Try to compile this example, and I hope you
> figure whats wrong with his proposal :
>
> int main()
> {
> int a = 3;
> switch( a )
> {
> case 3:
> a = 1;
> break;
> case 3:
> a = 2;
> break;
> case 3:
> a = 3;
> break;
> default:
> a = 4;
> }
> }
The OP was talking about /expressions/ for case values. But because normal
switch likes it's values to be unique, I suggested a different statement.
This way duplicate case values can still be picked up instead of being
quietly converted to the new semantics:
a=x=y=z=3;
newswitch (a)
{
case x: ...
case y: ...
case z: ...
...
This would just select the first matching expression. No different from a
series of if/else statements as already noted, but more eloquent.
--
Bartc
Yes, it would work, just as it does for COBOL's EVALUATE statement - a
multiway if-statement
http://www.fluffycat.com/COBOL/Evaluate/
Wanna be more like COBOL? :-)
Bo Persson
The statements following each case lable in a switch are intended to
be diffrent, while the body of for statement is the same for each
loop. Eric's trick illustrates that how bad an idean could that
proposal be.
The expressions (not statements) are intended (required, in fact) to
be integer constant expressions. The proposal is to change that
requirement. It's moderately obvious that the requirement that no two
of the case expressions would also have to be relaxed, since it's not
possible in general to determine at compilation time whether two
non-constant expressions have the same value.
Defining consistent semantics for a modified switch statement is not
all that difficult. Presumably existing switch statements would have
to behave as they do now. Beyond that, there are several possible
ways to define the semantics and constraints for switch statements
that are currently invalid.
Eric's example demonstrates the need to define the semantics of this
proposed new feature. I don't think it demonstrates, by itself, that
it's a bad idea. If non-constant switch expressions were allowed, it
would be possible to write perfectly sensible code using the new
feature. It would also, as Eric demonstrated, be possible to write
very silly code using the new feature -- or using any of the
language's existing features.
I do agree that the proposed feature is a bad idea, but not because of
Eric's example. I think it's a bad idea because it would have to
define new semantics for an existing construct. Currently, if you
accidentally use a non-constant case expression, the compiler will
diagnose it as an error (and probably reject your program). Under the
proposal, the code would be valid, and would likely behave in a subtly
wrong manner and/or with poor performance. Evaluating the switch
expression and branching to the right case label is typically an O(1)
operation, if the compiler is able to generate a jump table. Add a
single non-constant expression, and it becomes O(N), where N is the
number of cases.
I wouldn't object as strongly to a new feature, with a distinct
syntax, that implements the proposed functionality, but I'd still
oppose it -- not because it's an inherently bad idea, but because the
benefit is IMHO outweighed by the cost of adding a new feature to the
language. There's nothing you could do with the proposed new feature
that you can't already do fairly straightforwardly using existing
features, namely an if/else chain and, if the switch expression is
complicated or has side effects, a temporary variable.
This has well-defined semantics, albeit not very useful.
The purpose of my example wasn't to claim that it is impossible
to invent semantics for the O.P.'s `switch', but to show that
the proposal as it stands is not even close to adequate.
Let's try another one:
switch (x) {
case f(x): puts("f"); break;
case g(x): puts("g"); break;
}
...
int f(int x) { puts("in f"); return x & 1; }
int g(int x) { puts("in g"); return x & 2; }
What output do you desire when `x == 16'? Two lines?
Three? Four? In what order?
--
Eric Sosman
eso...@ieee-dot-org.invalid
Your code works better if it is made legal:
int main(void) {
int a = 3;
switch( a ) {
case 3: a = 1;
break;
case 2: a = 2;
break;
case 1: a = 3;
break;
default: a = 4;
}
return 0;
}
Also, cross posting to c.l.c++ is not cool.
>>> switch (rand()) {
>>> case rand(): puts ("What's"); break;
>>> case rand(): puts ("wrong"); break;
>>> case rand(): puts ("with"); break;
>>> case rand(): puts ("this"); break;
>>> case rand(): puts ("picture?"); break;
>> What's wrong with it that you're just injecting garbage. You can use the
>> same rand() trick with any statement:
>>
>> for (i=rand(); i<=rand(); i=rand())...
>
> This has well-defined semantics, albeit not very useful.
> The purpose of my example wasn't to claim that it is impossible
> to invent semantics for the O.P.'s `switch', but to show that
> the proposal as it stands is not even close to adequate.
>
> Let's try another one:
>
> switch (x) {
> case f(x): puts("f"); break;
> case g(x): puts("g"); break;
> }
> ...
> int f(int x) { puts("in f"); return x & 1; }
> int g(int x) { puts("in g"); return x & 2; }
>
> What output do you desire when `x == 16'? Two lines?
> Three? Four? In what order?
When x is 16, both f() and g() return 0, which does not equal x so matches
neither case.
You're thinking in terms of the current switch semantics which likes unique
case values and can take them in any order. That's why I suggested a
different statement (or just a different keyword).
(I tried this code in a language of mine that accepts expressions for case
expressions. The output was "in f" followed by "in g".
In this language there are two distinct statements: switch-when for integer
values over a small range,expected to be implemented as a jump table (ie. C
switch); and case-when for anything else: large integers or other types.
Although I don't think I've ever used a non-const expression for a
switch/case value. Until now)
--
Bartc
Which completely and utterly misses the point.
The article that started this thread was a proposal to allow
non-constant case expressions. anon's point was that it would then
become impossible (or nearly so) to maintain the property that all
case expressions in a switch statement must have distinct values.
Your "correction" is valid C, but pointless; you might as well have
posted "int main(void) { }".
> Also, cross posting to c.l.c++ is not cool.
Perhaps, but the topic of the thread is a proposed change to both C
and C++. Arguably the comp.std.* groups would have been better, but I
think comp.std.c++ is more or less dead these days. Since switch
statements have the same syntax and semantics in C and C++, it's not
entirely unreasonable to discuss a proposed change in both.
It's what Chuck does best. One should always play to one's strengths.
In what way would your altered switch be superior to an if/else tree?
The primary reason to use a switch is that it may be translated at
compile-time into a jump table, but that benefit goes away if the
statements are not known until run-time. Languages like Ruby have
switches with run-time case expressions because they delay the
translation until run-time, anyway; such languages are using the syntax
to mean something very different from what the C switch means. IOW,
languages that provide case statements of the form you've suggested do
so only because they *can't* do what C and C++ do.
[ ... ]
> int y, x = f();
> switch (x)
> {
> case 123 : y = g(); break;
> case g() : y = 456; break; // using new case feature, ie. func-call
> case h() : y = 789; break; // ditto
> default : y = -1; break;
> }
This has a number of ramifications that could make definition difficult.
For a few examples, how often would g() be evaluated in the switch
statement above? If both g() and h() returned the same value, which leg
would be taken?
--
Later,
Jerry.
The universe is a figment of its own imagination.
The person proposing the feature did not give enough information to
answer those questions. If the proposed feature were to be added to
the language, then the semantics would have to be worked out first;
doing so shouldn't be too difficult. Once that's done, the answers to
your questions become obvious rather than indeterminate. (Possibly in
some cases the execution would be implementation-defined, or
unspecified, or even undefined, but I'd prefer a simpler definition
that avoids that.)
In my preferred semantics, the above would be exactly equivalent to:
int y, x = f();
int __tmp = x;
if (__tmp == 123) y = g();
else if (__tmp == g()) y = 456;
else if (__tmp == h()) y = 789;
else y = -1;
The number of times g() is evaluated depends on the value of x and the
value returned by g(), which you haven't specified. If both g() and
h() return the same value, and that happens to be the value of x, then
y would be set to 456.
But, as I said, I think the whole thing is a bad idea.
How about:
cond {
(foo == bar): ...; break;
(!bar && (baz == boo)): ...; /* fallthru */
default: ...; break;
}
In pseudo-grammar:
cond { [<expr>|default: <stmt>*]* }
One advantage of the switch statement is that the expression only has
to be written once; it's implicitly compared to each of the case label
expressions without being re-evaluated.
Your proposed "cond" statement lacks this advantage, and differs from
an if/else chain only in the ability to fall through from one case to
the next. The latter can easily be done with a goto statement. (Yes,
gotos are ugly; so is falling through from one case to the next. And
you can always restructure the code to avoid gotos.)
As for the grammar, I think you'd want to require a keyword, probably
"case", for each expression:
cond {
case foo == bar: ... break;
case !bar && (baz == boo): ...; /* fallthrough */
default: ...; break;
}
Otherwise the compiler doesn't know until it sees the ':' whether the
expression is part of the cond statement or just an ordinary
expression statement.
And of course it would break every existing program that uses "cond"
as a keyword, but that's easily solved by using "_Cond".
> And of course it would break every existing program that uses "cond"
> as a keyword,
ITYM "as an identifier,".
mlp
D'oh! Yes, thanks.
> In what way would your altered switch be superior to an if/else tree?
Two ways:
1) An if/else tree hides the fact that the same variable is being
tested in each branch. A switch/case makes it clear that the same
expression is being compared in each case.
2) In some cases, it would make truly atrocious code look better. For
example, consider cases where you need to fall through from one case
to another or where you would otherwise need a temporary to avoid
multiple evaluations of the switch variable.
> The primary reason to use a switch is that it may be translated at
> compile-time into a jump table, but that benefit goes away if the
> statements are not known until run-time. Languages like Ruby have
> switches with run-time case expressions because they delay the
> translation until run-time, anyway; such languages are using the syntax
> to mean something very different from what the C switch means. IOW,
> languages that provide case statements of the form you've suggested do
> so only because they *can't* do what C and C++ do.
But they don't provide what he's asking for, and it's a natural
extension of the semantics of the switch/case statement. We could have
a special 'if' that allowed the implementation to cycle a specified
constant, either by counting up, down, or sideways if that was most
efficient. But we don't. We expect the optimizer to figure out how our
code can be micro-optimized.
Why do we have do/while, while, and for? You can make any code ugly by
choosing the wrong one, but you can make a lot of code nice by
choosing the best one.
The biggest argument against this proposal is that it's not really all
that useful. There really just aren't that many places where you could
use something like this.
In a 500,000 C++ line project I'm very familiar with, there are three
places where this could actually be useful. For comparison, it has 818
switch statements.
In two of those cases, it cleans up some ugliness where you need an if/
else tree before a switch/case statement.
What will be next? "case >=7:"?
DS
> How about:
> In pseudo-grammar:
I once designed a language with something like that, many, many
years back. In fact, the grammar was a bit more complete,
something like:
cond_stmt := 'cond' [<expr1>] '{' case_list '}'
case_list := /* empty */ | case_list case_clause
case_clause := 'case' [<op>] expr2 ':' stmt
| 'default' ':' stmt
If expr1 was absent, <op> was forbidden, and it worked exactly
like your suggestion (except that there was no fall through---a
case controlled exactly one statement). If expr1 was present,
it was the equivalent of having written "case <expr1> <op>
<expr2>" for each case, except that expr1 was only evaluated
once; if you omitted the <op> in this case, it defaulted to ==,
so you could write things like:
cond x {
case > 0 : ... ;
case == 0 : ... ;
case < 0 : ... ;
}
or
cond c {
case 'a' : ... ;
case 'b' : ... ;
case 'c' : ... ;
}
(IIRC, the keyword was actually select, and not cond, and I used
OF .. END instead of {..}. But the basic idea was the same.)
The idea was basically that there are only four basic structured
constructs: a loop, a choice, a sequence, and a procedure call,
and thus, there were only four basic execution statements.
--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Frankly I think ranges on the case constant expressions would be a
more useful addition while staying with the basic philosophy of the C
switch statement. IOW, "case 2...5:", or something along those
lines. But still not something I'm loosing sleep over...
Then programmers will inevitably write
case 'A' ... 'Z':
which is non-portable (under EBCDIC it matches '\' and '}').
In which case, it would be relatively easy to add syntax similar to:
case [A-Z]:
Which would, of course, be portable.
SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
GCC provides this as an extension, FWIW. You can write
case 2 ... 5:
The spaces are required to keep the parser from thinking it's some
malformed floating point constant.
It could be, if you defined it *very* carefully.
Lexically, you have 7 tokens:
case (a keyword)
[ (a punctuator)
A (an identifier)
- (a punctuator)
Z (an identifier)
] (a punctuator)
: (a punctuator)
Do you really intend the identifier A here to refer to the character
'A', ignoring any declared entity called A? Do you really intend to
specify a range of literal character values without using the "'"
symbol? Which characters would be allowed? Does [A-Z] refer only to
the 26 uppercase letters of the Latin alphabet? Could this notation
be used for non-Latin letters? Digits? Punctuation symbols?
Properly defined, it could make it easier to work with Latin letters
-- which is both good and bad, since it would further encourage
programmers to ignore the fact that other alphabets exist. Sometimes
you really do want to determine whether a character matches one of the
26 uppercase Latin letters, but more often what you *really* want is
the locale-sensitive behavior of isupper().
Well, no. That's why I stated 'similar to'. It's just an idea after all.
) programmers to ignore the fact that other alphabets exist. Sometimes
) you really do want to determine whether a character matches one of the
) 26 uppercase Latin letters, but more often what you *really* want is
) the locale-sensitive behavior of isupper().
Well, then how about
case [:upper:]:
Which, to be honest, is half-borrowed from Perl syntax.
What the exact syntax is, isn't really relevant, I guess.
The syntax is less important than getting the semantics
right. One problem is in how to define the character classes;
note that isupper() et al. are locale-dependent, so the set of
characters that satisfy them is hard to compute at compile time.
And there's still the issue of ambiguous branches:
switch ('a') {
case isalpha(ch): ...
case islower(ch): ...
case isxdigit(ch): ...
case !ispunct(ch): ...
"These arguments sound very well, but I can't help
thinking that, if they were reduced to syllogistic form,
they wouldn't hold water." -- R. Murgatroyd
Oh, botheration: I botched that one royally, didn't I? The
fragment ought to have been something like
ch = 'a';
switch (0) {
case !isalpha(ch): ...
case !islower(ch): ...
case !isxdigit(ch): ...
case !!ispunct(ch): ...
They do anyway. #define ISUPPER(c) ('A' <= (c) && (c) <= 'Z'). E.g. to
check for the ASCII (or 7-bit Unicode) letters regardless of locale.
> which is non-portable (under EBCDIC it matches '\' and '}').
--
Hallvard
Sure, but there's not much that can be done to discourage dumb macros.
Actually, there is: <ctype.h> already has the locale-sensitive
isupper() function.
The problem with the proposed "..." notation is that it makes it easy
to do the wrong thing (assuming that the uppercase letters have
contiguous codes and run from 'A' to 'Z') *without* making it any
easier to do the right thing (using isupper() to determine whether a
character is an uppercase letter).
On the other hand, there are certainly times when case ranges would be
handy for numeric ranges, as opposed to character ranges, and using
them wouldn't cause any problems. Other languages do provide similar
constructs. And I suppose compilers could warn about 'A' ... 'Z'.
Caveat: Sometimes ('A' <= c && c <= 'Z') *is* exactly what you want,
if you're writing deliberately non-portable code.
That was already included in microsoft fortran 5.1 (extension)
in 1990 :) :)
Pascal had it long before that, and it's likely that other languages
had it even earlier. There's certainly ample precedent. The question
is whether the benefits would outweigh the disadvantages, especially
considering the ambiguity of ranges like 'A' ... 'Z'.
And why exactly would that be worse than an 'if'-'else' chain
relying on ASCII?
Schobi
An if-else chain in source code wouldn't rely on ASCII, but on the
source character set, which would, presumably, be EBCDIC when you're
targeting a machine that uses EBCDIC.
switch(ch)
{
case 'A':
case 'B':
case 'C':
case 'D':
...
}
Each character will be properly encoded, even though there are extra
characters in the middle of the capital letters in EBCDIC.
--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)
I'd expect most of the programmers I worked with, in the
absent of the 'switch' syntax proposed above, to write
if( x>='A' && x<='Z' )
which is just as wrong.
So allowing this for #switch' IMO wouldn't make anything
worse.
Schobi
It wouldn't. The problem (as I think I've already said in this
thread) is that adding this syntax makes it much easier to check for
characters in the range 'A' to 'Z' *without* making it any easier to
check for characters that are uppercase letters.
It's not the least bit difficult to write bad code in C or C++ (this
is cross-posted), even with their current features, but let's not make
it even easier.
It doesn't need to be *deliberately* non-portable. If you were
implementing your own C library, on an ASCII-based machine with minimal
locale support, this could be the best way to write isupper.
> )> case [A-Z]:
> )>
> )> Which would, of course, be portable.
> case [:upper:]:
>
> Which, to be honest, is half-borrowed from Perl syntax.
> What the exact syntax is, isn't really relevant, I guess.
It wouldn't be relevant, except that there is a widely recognized
standard (POSIX). It is already largely supported in C++ by Boost.Regex:
http://www.boost.org/doc/libs/1_36_0/libs/regex/doc/html/boost_regex/syntax/character_classes.html
I'd call that deliberately non-portable, at least if you know what
you're doing.
I read your message as suggesting non-portable had to be a goal for 'A'<=c
&& c<='Z' to be the right thing. If you meant that non-portable can be
okay, just that you need to be aware of it, then agreed.
Probably not. A table of flags is best, because it handles all the
isxxx functions.
Exactly. Portability is good, all else being equal, but all else is
not always equal.
Then I don't see how your above argument is valid.
Schobi
Since you snipped my argument, I have no idea why you disagree with
it.
Funny. My newsreader still shows it.
Schobi
I was referring to the text you replaced with "[...]".
I'm going to restore mr Thompsons text: (it's where you have [...])
> [It wouldn't.] The problem (as I think I've already said in this
> thread) is that adding this syntax makes it much easier to check for
> characters in the range 'A' to 'Z' *without* making it any easier to
> check for characters that are uppercase letters.
> It's not the least bit difficult to write bad code in C or C++ (this
> is cross-posted), even with their current features, but let's not make
> it even easier.
My interprentation:
He says that adding such syntax, .., would introduce more newbie
pitfalls in the language, because most people would expect 'A' .. 'Z'
to mean all the uppercase letters, where it could mean anything.
My view on it: Syntactic sugar that would only complicate the
language(s) further without being a real convenience.
And wouldn't those be the people who now use 'if' to
do exactly the same?
> My view on it: Syntactic sugar that would only complicate the
> language(s) further without being a real convenience.
I'm not a string proponent of the feature. I just don't
see the validity of this argument against this.
Any feature added to the language will open new ways to
abusing. If that by itself was an argument against new
features, we wouldn't have had any features at all.
Schobi
Probably. But my point, one more time, is that this feature would, in
this particular case, make it *easier* to write bad code (i.e., code
that assumes 'A'-'Z' are contiguous) without making it any easier to
wrote good code (i.e., code that avoids that assumption by using
isupper(), which can't be used in a case expression).
Suppose you're maintaining some code that uses
case 'A' ... 'Z':
and you want to make it more general. You'll have to restructure the
code, using an if statement rather than a switch statement.
On the other hand, if the code you're maintaining uses the equally
non-portable:
if ('A' <= c && c <= 'Z') ...
then it's much more straightforward to change it to:
if (isupper(c)) ...
or perhaps
if (isupper((unsigned char)c)) ...
I'm not saying this is an ironclad argument against adding such a
feature, just that it's a (possibly) significant issue that should be
considered.
>> My view on it: Syntactic sugar that would only complicate the
>> language(s) further without being a real convenience.
On the other hand, it *could* be a real convenience when you need
numeric ranges. It would also be perfectly ok for '0' ... '9', or
even for 'A' ... 'Z' if portability isn't a high priority.
> I'm not a string proponent of the feature. I just don't
> see the validity of this argument against this.
> Any feature added to the language will open new ways to
> abusing. If that by itself was an argument against new
> features, we wouldn't have had any features at all.
Sure, any feature can be abused; the problem is that this feature
makes certain kinds of abuse easier without making the corresponding
non-abuse any easier.
What would be a corresponding non-abuse?
Schobi
if(isupper(c))