case labels

A

aarklon

Hi,

why general integer expressions are not allowed in case labels in
switch statements..????
 
I

Ian Collins

*Please* don't quote signatures.
Could you elaborate this concept?

The language requires case labels to be compile time constants. There
really isn't a concept to elaborate on.
 
K

Keith Thompson

Ian Collins said:
*Please* don't quote signatures.

The language requires case labels to be compile time constants. There
really isn't a concept to elaborate on.

Unless you want to know *why* the standard imposes this requirement.

The code generated for a switch statement is typically a static jump
table (though it doesn't have to be). The advantage of this is that
the code doesn't have to traverse through various possible values of
the controlling expression to determine where to jump; it's typically
faster than an equivalent if/else chain. The disadvantage is that the
values for all the labels have to be known at compile time so the
compiler can construct the jump table.

Having said that, there's nothing forbidding a compiler from treating
a switch statement like an if/else chain anyway (and it will probably
have to do something like that if the range of cases is very large).
 
U

user923005

Unless you want to know *why* the standard imposes this requirement.

The code generated for a switch statement is typically a static jump
table (though it doesn't have to be).  The advantage of this is that
the code doesn't have to traverse through various possible values of
the controlling expression to determine where to jump; it's typically
faster than an equivalent if/else chain.  The disadvantage is that the
values for all the labels have to be known at compile time so the
compiler can construct the jump table.

Having said that, there's nothing forbidding a compiler from treating
a switch statement like an if/else chain anyway (and it will probably
have to do something like that if the range of cases is very large).

A smart compiler would sort the labels and do a bsearch to find the
desired element.
Or it could compute a perfect hash at compile time.
 
B

Bartc

Hi,

why general integer expressions are not allowed in case labels in
switch statements..????

There's no reason why switch expressions can't be any (identical) type, as
Keith has suggested.

If they are all constant integers, then fine generate a jump table.
Otherwise generate appropriate if-statements. This is very easy at a time
when C compilers are so super-sophisticated they can almost bake bread too.

But the people responsible for new C standards have decided to keep this
area of the language (control statements in general) at a primitive level.

So the programmer has to code his logic in a less expressive manner.
 
E

Eric Sosman

Bartc said:
There's no reason why switch expressions can't be any (identical) type, as
Keith has suggested.

If they are all constant integers, then fine generate a jump table.
Otherwise generate appropriate if-statements. This is very easy at a time
when C compilers are so super-sophisticated they can almost bake bread too.

But the people responsible for new C standards have decided to keep this
area of the language (control statements in general) at a primitive level.

So the programmer has to code his logic in a less expressive manner.

If you switch on a double, with double case labels,
what's the matching criterion? Exact equality is seldom
useful, and it's not clear how a suitable "fuzz factor"
should be provided. Case label ranges, perhaps, but what
to do with infinities and NaNs? (Can a NaN be matched
at all, other than by the default case?)

If you switch on a struct-valued expression, what's
the matching criterion? Must all the struct's elements
match, or would you want to designate firstname,lastname
as a "key pair?" Are you willing to define sub-structs
in all your structs (perhaps permuting the element order)
just so you can switch on them? What if you want to use
overlapping but different subsets of elements in different
switch statements?

If you switch on a `char*', should the matching
criterion be strcmp()? Or caseInsensitiveStrcmp()? Or
allWhiteSpaceCountsAsIdenticalStrcmp()?

If you've got ideas of how to handle questions of this
kind, get hold of the gcc source and implement your answers.
Then float it around to see whether people like it; if
they do, you'll have taken a big step towards getting it
into a revised Standard -- "prior art" is a lot more
convincing than "I wish."
 
B

Bartc

Eric Sosman said:
If you switch on a double, with double case labels,
what's the matching criterion? ....
If you switch on a struct-valued expression, what's
the matching criterion? ....
If you switch on a `char*', should the matching
criterion be strcmp()?

Yes I know there's all sorts of obscure examples where such a switch would
be ambiguous, but in that case no advance would ever be made.

Probably all the OP wants is to be have variable int cases, then an
extension of switch which simply applies the == operator would be a useful
advance.

Another simple enhancement is a range operator (maybe gcc has this?) like
case 'A'..'Z':, which in the case of doubles could translate into >= and <=.
And with char* strings, the compiler can apply some common sense and use
strcmp(). There are ways.
If you've got ideas of how to handle questions of this
kind, get hold of the gcc source and implement your answers.
Then float it around to see whether people like it; if
they do, you'll have taken a big step towards getting it
into a revised Standard -- "prior art" is a lot more
convincing than "I wish."

I've worked on a compiler for a language where something similar is possible
(although I found it easier to have two forms of switch: one with constant
int cases, and one with any simple type where =/== made sense). And it was
workable.

But no I don't fancy delving into the gcc sources!
 
C

Chris Dollin

Eric Sosman wrote:

(fx:dreaming)
If you switch on a struct-valued expression, what's
the matching criterion?

struct_Foo_switch_equality
( struct Foo *switched, struct Foo *cased );

struct_Foo_hash_value
( struct Foo *foo );
If you switch on a `char*', should the matching
criterion be strcmp()? Or caseInsensitiveStrcmp()? Or
allWhiteSpaceCountsAsIdenticalStrcmp()?

strcmp. If the user wants something else, they can canonise
their switched string first. (And I'm imagining a `strhash`.)

I grant that an implementation is more convincing than a handwave is.

Getting non-unfortunate performance might be more of a problem than
mere semantics. (Hence the hashes.)

--
"Its flagships were suns, its smallest vessels, /The City and the Stars/
planets."

Hewlett-Packard Limited registered office: Cain Road, Bracknell,
registered no: 690597 England Berks RG12 1HN
 
E

Eric Sosman

Bartc said:
Yes I know there's all sorts of obscure examples where such a switch would
be ambiguous, but in that case no advance would ever be made.

The question is whether the "advance" produces something
useful, and the situations I mentioned seem to me like real-
life problems the enhanced switch would have trouble with.
Probably all the OP wants is to be have variable int cases, then an
extension of switch which simply applies the == operator would be a useful
advance.

The O.P. in this case asked a very short question, from
which you seem to derive a lot more information than I can.

Besides, I'm still not convinced of the utility, even
if restricted to int values. Try this:

int x = 42, y = 43;
for (int i = 40; i < 50; ++i) {
switch (i) {
case x: printf("x\n"); break;
case y: printf("y\n"); break;
default: printf("?\n"); break;
}
y = 42;
}

Worse still, try this:

int c1, c2;
c1 = getchar();
switch (c1) {
case EOF: printf("done!\n"); return;
case c2 = getchar():
printf("two %c's in a row\n", c1);
break;
default:
ungetc(c2); break;
}

Question: Is the "internal" getchar() executed if c1==EOF?

Another simple enhancement is a range operator (maybe gcc has this?) like
case 'A'..'Z':, which in the case of doubles could translate into >= and <=.

Yes, gcc has an extension like this. And yes, it's less
useful than one might think. For example, the case you show
does necessarily select all upper-case letters, nor does it
necessarily exclude all non-letters. What good is it?

Case ranges might be handy in some other situations, but
it seems suggestive that the example always offered for them
is the one you've shown, where ranges are clearly *not* the
right thing to use.
And with char* strings, the compiler can apply some common sense and use
strcmp(). There are ways.

... so the enhanced switch is limited to just one notion
of matching. True, it's probably the most "natural" notion
for strings, but it's not the only notion. Also, what do
you get if the char* you switch on (or one of the labels)
is NULL? What if it's just a char* that points at a char,
but not at the start of a string?
I've worked on a compiler for a language where something similar is possible
(although I found it easier to have two forms of switch: one with constant
int cases, and one with any simple type where =/== made sense). And it was
workable.

I'm not saying that definitions could not be invented to
nail down these loose bits; other languages have certainly
come up with switch-ish constructs that handle at least some
of the difficulties. But is it worth the trouble? I'm not
sure -- and that's why I suggested doing an implementation
and finding out whether people would use the feature in
interesting ways.
> But no I don't fancy delving into the gcc sources!

Well, if the advance isn't worth some fairly serious
work, that may mean the need it addresses isn't too urgent.
 
C

CBFalconer

why general integer expressions are not allowed in case labels in
switch statements..????

Because a common switch implementation is an indexed jump through a
fixed table of destinations.
 
C

CBFalconer

user923005 said:
A smart compiler would sort the labels and do a bsearch to find
the desired element. Or it could compute a perfect hash at
compile time.

Please apply that thought to 'general integer expressions'.
 
L

lawrence.jones

Keith Thompson said:
Having said that, there's nothing forbidding a compiler from treating
a switch statement like an if/else chain anyway (and it will probably
have to do something like that if the range of cases is very large).

Indeed, most compilers have a number of different strategies for
implementing switch statements that they choose from based on things
like the number of cases, their range, and density.

-Larry Jones

When I want an editorial, I'll ASK for it! -- Calvin
 
A

aarklon

*Please* don't quote signatures.




The language requires case labels to be compile time constants. There
really isn't a concept to elaborate on.

The real elaborate question i wanted to ask was

why are constant integer expressions required in case labels of the
switch statement? what would be the impact of allowing general integer
expressions instead of constant integer expressions? discuss both user
convenience and implementation aspects?
 
R

Richard Tobin

The real elaborate question i wanted to ask was

why are constant integer expressions required in case labels of the
switch statement? what would be the impact of allowing general integer
expressions instead of constant integer expressions? discuss both user
convenience and implementation aspects?

Why not run a C course and ask your students?

-- Richard
 
B

Bartc

Eric said:
The question is whether the "advance" produces something
useful, and the situations I mentioned seem to me like real-
life problems the enhanced switch would have trouble with.


The O.P. in this case asked a very short question, from
which you seem to derive a lot more information than I can.

He or she asked why general int expressions weren't allowed. Either this was
out of curiosity or an attempt was made to use them. Or, unlikely, constant
int expressions were tried on a compiler than didn't reduce them.
Besides, I'm still not convinced of the utility, even
if restricted to int values. Try this:

int x = 42, y = 43;
for (int i = 40; i < 50; ++i) {
switch (i) {
case x: printf("x\n"); break;
case y: printf("y\n"); break;
default: printf("?\n"); break;
}
y = 42;
}
int c1, c2;
c1 = getchar();
switch (c1) {
case EOF: printf("done!\n"); return;
case c2 = getchar():
printf("two %c's in a row\n", c1);
break;
default:
ungetc(c2); break;
}

Question: Is the "internal" getchar() executed if c1==EOF?

To get unambiguous semantics it would be necessary to assume the same
behaviour as equivalent if statements:

if (i==x) printf("x\n");
elsif (i==y)...

The answer to your question should be No, unless you reorder.

One trouble spot I can see is if, with a mix of variable and constant
expressions, an implementor mixes if-semantics with a jump table. Reordering
would be necessary (all variables first), so that a variable with the same
value as a constant, will be matched first, even if it's in a later case.
Also I haven't thought about break (or the absence of it).

(With 1000 constant case expressions and one variable one, it would be
tempting to include a jump table.)

Despite all this, I can't find a single use of a variable switch-case
expression (or when-expression in that other language) in my code!

So I can understand why switch has been left alone. Nevertheless, this other
language does allow variables ints (or constant/variable other types) if
they are ever needed.

Using doubles needn't be fraught with problems either:

double x;
switch (x) {
case 0.0: puts("Exactly zero"); break;
case 1.0: puts("Exactly one"); break;
default: puts("Whatever x was, it wasn't exactly 0 or 1");
};
Yes, gcc has an extension like this. And yes, it's less
useful than one might think. For example, the case you show
does necessarily select all upper-case letters, nor does it
necessarily exclude all non-letters. What good is it?

I would use it if I know the switch-expression is an ASCII code.
... so the enhanced switch is limited to just one notion
of matching. True, it's probably the most "natural" notion
for strings, but it's not the only notion. Also, what do
you get if the char* you switch on (or one of the labels)
is NULL?

What does strcmp() do if one of the parameters is NULL? If NULL is a problem
here, the compiler must use a safer version of strcmp(), and in this case I
would enhance it so that NULL and "" will match.
What if it's just a char* that points at a char, but not at the start of a
string?

That's a mild deficiency of C, not being able to distinguish between a
string and a pointer to one character. I would go with strcmp() still. For
anything else, cast to *void, then just pointers are compared.
... But is it worth the trouble? I'm not
sure -- and that's why I suggested doing an implementation
and finding out whether people would use the feature in
interesting ways.


Well, if the advance isn't worth some fairly serious
work, that may mean the need it addresses isn't too urgent.

I wasn't suggesting originally that C should be changed. Just pointing out
that variable case-expressions aren't allowed even though it would have been
comparatively straightforward (compared to some of the stuff in C++) to
allow them.

I know I wouldn't single-handedly be able to change anything in C, it would
be near impossible.
 
J

jacob navia

Indeed, most compilers have a number of different strategies for
implementing switch statements that they choose from based on things
like the number of cases, their range, and density.

Yes. Lcc-win will produce a if/else if the number of case
is less equal to 3.
 
L

lawrence.jones

why are constant integer expressions required in case labels of the
switch statement? what would be the impact of allowing general integer
expressions instead of constant integer expressions? discuss both user
convenience and implementation aspects?

Restricting case labels to integer constant expressions allows various
highly efficient implementations. For general expressions, there's no
more efficient implementation than a sequence of if ... else if ...
statements. Since you can already write the sequence of statements if
you need it, allowing you to write it as a switch as well wouldn't add
much expressive power but it would complicate the description of the
language and the implementation of the compiler somewhat. DMR decided
that the benefit wasn't worth the cost.

-Larry Jones

Nothing spoils fun like finding out it builds character. -- Calvin
 
J

jacob navia

Restricting case labels to integer constant expressions allows various
highly efficient implementations. For general expressions, there's no
more efficient implementation than a sequence of if ... else if ...
statements. Since you can already write the sequence of statements if
you need it, allowing you to write it as a switch as well wouldn't add
much expressive power but it would complicate the description of the
language and the implementation of the compiler somewhat. DMR decided
that the benefit wasn't worth the cost.

-Larry Jones

Nothing spoils fun like finding out it builds character. -- Calvin

This is correct. But I think allowing case *ranges* would not affect
the generality of the optimization. lcc-win, for instance, generates
several secondary tables in the optimized search tree, when there are
different disjoint ranges of case values.

case 1: case 2: case 3:
//
break;

case 1459: case 1460: case 1461:
//
break;


We could write the same switch with less effort if we had:

case 1...3:
//
break;
case 1459...1461:
//
break;

The ellipsis token exists already. Nothing would be more easy to do.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top