Why constant ints in switch case expressions?

  • Thread starter Brian J. Sayatovic
  • Start date
B

Brian J. Sayatovic

I've always been bothered by the restriction that case statements must
have constant integer values for their case expressions. That is, the
following is impossible:

int x = 10, y = 10;
switch(x) {
case y:
System.out.println("y=10");
break;
default:
System.out.println("y!=10");
break;
}

I could very easily turn that into cascading if statements:

int x = 10, y = 10;
if(x == y) {
System.out.println("y=10");
}
else {
System.out.println("y!=10");
}

So what is it about a switch statement that requires this restriction?
I would hope that there is some optimization or other advantage to
compensate for this disadvantage (really a pain when doing type-safe
enums, but hopefully JDK 1.5 will make that a moot point).

A VB programmer (yes, VB) once suggested that the compiler could
relate incoming values to instruction locations so that it wouldn't
have to test each value but merely jump directly to the location
needed. I tend to think that would be impossible since the incoming
value could take on any value.

Can someone enlighten me?

Regards,
Brian.
 
R

Roedy Green

So what is it about a switch statement that requires this restriction?

Laziness on the part of the compiler writers/language designers. When
the switch constants are variables, the switch has to be implemented
differently than when you know all the values at compile time.

When the values are known at compile time and don't change, you can
compose a jump table, i.e. a array of addresses of code to execute.

With a tiny bit more ambition on the part of compiler writers, ranges
too could also be handled much more efficiently than typically hand
coded nested ifs.

See http://mindprod.com/projcasefixer.html
http://mindprod.com/projcaserange.html
http://mindprod.coom/bali.html#EXTENDEDCASE
 
J

John C. Bollinger

Roedy said:
Laziness on the part of the compiler writers/language designers. When
the switch constants are variables, the switch has to be implemented
differently than when you know all the values at compile time.

When the values are known at compile time and don't change, you can
compose a jump table, i.e. a array of addresses of code to execute.

Which ought to perform better than a series of ifs. I personally have
no objection to having to write things in if form when I am testing
against variable alternatives.
With a tiny bit more ambition on the part of compiler writers, ranges
too could also be handled much more efficiently than typically hand
coded nested ifs.

That is a feature I would love to have. (And it has been available in
Fortran since Fortran-90, BTW. I don't know how Sun lives with itself,
knowing that Java is trailing behind Fortran. :) )


John Bollinger
(e-mail address removed)
 
D

David Zimmerman

Brian said:
I've always been bothered by the restriction that case statements must
have constant integer values for their case expressions.
So what is it about a switch statement that requires this restriction?
I would hope that there is some optimization or other advantage to
compensate for this disadvantage (really a pain when doing type-safe
enums, but hopefully JDK 1.5 will make that a moot point).

A VB programmer (yes, VB) once suggested that the compiler could
relate incoming values to instruction locations so that it wouldn't
have to test each value but merely jump directly to the location
needed. I tend to think that would be impossible since the incoming
value could take on any value.

There are two byte code instructions to do switchs, lookupswitch (which
has a table of value/offset pairs) and tableswitch (which has just a
jumptable). The compiler uses one or the other depending on the shape of
the values on the case statements.
 
D

Doug Pardee

I've always been bothered by the restriction that case statements must
have constant integer values for their case expressions.
[snip]
So what is it about a switch statement that requires this restriction?

James Gosling is the person who knows why Java was designed the way
that it was. I've never seen him post to the Usenet Java groups.
Anyone here can only guess.
I would hope that there is some optimization or other advantage to
compensate for this disadvantage

The bytecodes for handling switches -- tableswitch and lookupswitch --
require integer values. Anything else would have to be turned into an
if/elseif by the compiler.
 
B

brougham3

I've always been bothered by the restriction that case statements must
have constant integer values for their case expressions. That is, the
following is impossible:

int x = 10, y = 10;
switch(x) {
case y:
System.out.println("y=10");
break;
default:
System.out.println("y!=10");
break;
}

I could very easily turn that into cascading if statements:

int x = 10, y = 10;
if(x == y) {
System.out.println("y=10");
}
else {
System.out.println("y!=10");
}

You've already gotten some answers about how the compiler can optimize the
switch if the values are known at compile time.

In an object-oriented language, a large cascading if structure is often a
sign of a poor design.

I'd suggest that for values which aren't known 'til run time, see if you can
use a HashMap or something similar to solve your problem. Change it back to
cascading ifs only if performance testing dictates such after you have
finished implementing.
 
D

dhek bhun kho

That is a feature I would love to have. (And it has been available in
Fortran since Fortran-90, BTW. I don't know how Sun lives with itself,
knowing that Java is trailing behind Fortran. :) )

It would save some typing I guess. Does Fortran do things like the
EVALUATE in COBOL?

EVALUATE x ALSO y ALSO z ALSO TRUE
WHEN 1 ALSO 2 ALSO 3 THRU 10 ALSO fun
PERFORM haha
WHEN 2 ALSO 3 ALSO 11 THRU 12 ALSO fun
PERFORM hahaha
END-EVALUATE

Haven't seen anything like this in any other other language.

Bhun.
 
J

John C. Bollinger

dhek said:
"John C. Bollinger" <[email protected]>, Wed, 02 Jul 2003 09:56:26
-0500:




It would save some typing I guess. Does Fortran do things like the
EVALUATE in COBOL?

EVALUATE x ALSO y ALSO z ALSO TRUE
WHEN 1 ALSO 2 ALSO 3 THRU 10 ALSO fun
PERFORM haha
WHEN 2 ALSO 3 ALSO 11 THRU 12 ALSO fun
PERFORM hahaha
END-EVALUATE

Haven't seen anything like this in any other other language.

Fortran's version is structurally more like C and Java. Example:

select case (foo)
case :)-1)
do stuff
case (0, 2, 4, 6:57)
do something else
case default
do something different
end select

The Fortran version has no fall-through. It supports a very flexible
and IMO intuitive syntax including open- and closed-ended value ranges
and sequences of values and value ranges. It also supports character
case values (the Fortran analogue of Java Strings) but not mixed with
integer case values or selection expressions. I think it would be a
inappropriate for Java to tie the String class to a control construct,
but it sure would be convenient to have something similar to the Fortran
flavor of expressing case values.

But lest we miss an important point, do I understand that Java is also
lagging behind COBOL?!? ;-)


John Bollinger
(e-mail address removed)
 
D

dhek bhun kho

Hello John,

Fortran's version is structurally more like C and Java. Example:

select case (foo)
case :)-1)
do stuff
case (0, 2, 4, 6:57)
do something else
case default
do something different
end select

The Fortran version has no fall-through. It supports a very flexible

Sorry, I do not understand what you mean with fall-through. (Not a native
speaker).
and IMO intuitive syntax including open- and closed-ended value ranges
and sequences of values and value ranges. It also supports character
case values (the Fortran analogue of Java Strings) but not mixed with
integer case values or selection expressions. I think it would be a
inappropriate for Java to tie the String class to a control construct,

Isn't this related to the fact that although two Strings might have the
same content, they are not always equal? (Unless you make sure they are
always internalized).

I do find it a loss, that Java does not support ranges (eg.
Modula-2) or enumerations. You can always code it as a
separate class, but it's awkward.
but it sure would be convenient to have something similar to the Fortran
flavor of expressing case values.

Is anything like this coming up in the 1.5 release?
But lest we miss an important point, do I understand that Java is also
lagging behind COBOL?!? ;-)

Hahaha. YES! :) Just kidding. I like Java, while it is not the end of the
world or anything, it's a nuisance to keep typing thing like MOVE ADD
CALCULATE and such.

What I liked about COBOL was how easy it is to do file operations. Yes,
they are platform dependent, but coding a persistent data structure was
almost trivial in COBOL. I have not seen any COBOL-2000 code though.

I find it odd that there are no standard facilities for easy persistence
with low memory usage. Everything is serial(!). As soon I want to perform
random access, I have to code every detail; or resort to a database
solution. (I am not coding any application creating 2e6 application). The
other solution would be read the whole file into memory, but that's like
swatting a fly with a sledge hammer.

It seems a lot easier to write the persistence code in COBOL, write some
JNI wrappers and call it from Java. I would not want to code an user
interface in COBOL.
John Bollinger
(e-mail address removed)

Greets.
Bhun.
 
J

Jezuch

U¿ytkownik (e-mail address removed) napisa³:
In an object-oriented language, a large cascading if structure is often a
sign of a poor design.

I'd suggest that for values which aren't known 'til run time, see if you can
use a HashMap or something similar to solve your problem. Change it back to
cascading ifs only if performance testing dictates such after you have
finished implementing.

Even better - try to use inheritance and polymorphism.
 
J

Joona I Palaste

dhek bhun kho said:
Hello John,
Sorry, I do not understand what you mean with fall-through. (Not a native
speaker).

Fall-through means that when a matching case is found, that case *AND*
all cases after it are executed. Compare Pascal:

case (a) of
1: println('Foo');
2: println('Bar');
end;

with C:

switch (a) {
case 1: printf("Foo\n");
case 2: printf("Bar\n");
}

Suppose a has the value 1. The Pascal version will only print "Foo" but
the C version will print both "Foo" and "Bar". If a has the value 2 both
Pascal and C will only print "Bar".
Java works like C, i.e. it has the fall-through.
Isn't this related to the fact that although two Strings might have the
same content, they are not always equal? (Unless you make sure they are
always internalized).

They are not equal in the sense that they are not the same object, but
equals() will still consider them equal.
I do find it a loss, that Java does not support ranges (eg.
Modula-2) or enumerations. You can always code it as a
separate class, but it's awkward.

Isn't the whole idea of object orientation that you can always code a
class for anything?

(snip about COBOL)

--
/-- Joona Palaste ([email protected]) ---------------------------\
| Kingpriest of "The Flying Lemon Tree" G++ FR FW+ M- #108 D+ ADA N+++|
| http://www.helsinki.fi/~palaste W++ B OP+ |
\----------------------------------------- Finland rules! ------------/
"Shh! The maestro is decomposing!"
- Gary Larson
 
B

Brian J. Sayatovic

Certainly, but that was not the point.

rkm said:
Brian said:
I've always been bothered by the restriction that case statements must
have constant integer values for their case expressions.
[snip]

doesn't it seem obvious that with your idea you could code
this indeterminant bit of code?
 
T

Tim Tyler

: Brian J. Sayatovic wrote:
:> I've always been bothered by the restriction that case statements must
:> have constant integer values for their case expressions. [...]

: doesn't it seem obvious that with your idea you could code
: this indeterminant bit of code?

: int x = 10, y = 10;
: switch(x)
: {
: case x:
: System.out.println("x=10");
: break;
: case y:
: System.out.println("y=10");
: break;
: default:
: System.out.println("x!=10 and y!=10");
: break;
: }

: Hmmm, where would it go, to case x or case y?

case x.
 
R

rkm

You asked "So what is it about a switch statement that
requires this restriction?", and I think I'm the only one
that came up with a valid reason why you shouldn't be able
to do what you asked. Others either agreed with you or
declared it was because compiler writers are lazy. I think
they missed the point.

Rick

Certainly, but that was not the point.

rkm said:
Brian said:
I've always been bothered by the restriction that case statements must
have constant integer values for their case expressions.

[snip]


doesn't it seem obvious that with your idea you could code
this indeterminant bit of code?
 
M

Miguel De Anda

rkm said:
doesn't it seem obvious that with your idea you could code
this indeterminant bit of code?


int x = 10, y = 10;
switch(x)
{
case x:
System.out.println("x=10");
break;
case y:
System.out.println("y=10");
break;
default:
System.out.println("x!=10 and y!=10");
break;
}

Hmmm, where would it go, to case x or case y?

Rick

Why wouldn't it just go to the first case?
 
P

pete kirkham

rkm said:
It could go to case x, but can you imagine the maintenance headaches
you're going to have when some new person comes along and wants to add
another case to this?

I the code was meant to, as the println statements suggest to be the same as

switch(x) {
case 10:
System.out.println("x=10");
break;

then it wouldn't compile, as the next case would also be 10, as y is 10.

On the other hand, if you intended the comparison to be made with the
runtime value of x, you perhaps should give an example with meaningful
results.

In Java, the fall through implies an ordering to the case labels. This
ordering would be such that the mapping of a non-constant case to a
cascade of 'if's would be the same as that of a const case; ie:

switch (X) {
case A:
default:
a();
break;
case B:
b();
case C:
c();
}

would be equivalent to

switch_1:
do {
do {
if (X==A) continue;
switch_2:
do {
do {
if (X==B) {
b();
continue;
}

if (X==C) {
continue;
}
break switch_2;
} while (false);
} while (false);
c();
break switch_1;
} while (false);
a();
} while (false);

In terms of maintainance, the former wouldn't be any harder to maintain
for A, B, C variable or constant, as the order is fixed. It also, at
least to my eyes, is somewhat clearer.

In the example you gave, 'X' is 'x' and 'A' is 'x' so the question of
where to place the additonal statement is trivial- if you want it to
have an effect, it has to be before the first one, because that is
always true.
What's he/she going to do, hunt down all the
callers of the module to see who's passing in which values and
then try to predict where to place the new case label?

Once such a construct has well defined semantics, the question is trival.
This poor person will
never know if the new code is going to execute unless the new case is
placed at the top to pre-empt all the other labels. How bug prone is
that?

The bugs would be no worse that those in

do {
if (x==x) {
System.out.println("x=10");
break;
}
if (x==y) {
System.out.println("y=10");
break;
}
System.out.println("x!=10 and y!=10");
break;
} while (false);

ie not in the syntax, but in the thinking that (x==x) -> x=10
Even in the simple hardcoded example above, if you think case x
should be the logical choice, then how do you ever get case y to
execute?

in the example
if (x==x) {
foo();
} else {
bar();
}

how would you ever get bar to execute? Does this mean that if statements
are inherently bug prone and a maintainance nightmare?

I don't think you can without something like a goto. I guess
you could say, well people shouldn't write code like that, but we all
know that if the compiler let's you, then someone will.

In terms of 'what behaviour the compiler lets you do', we already have
contructs of this power, which erm, just don't look as nice.


Pete
 
R

rkm

pete said:
I the code was meant to, as the println statements suggest to be the same as

switch(x) {
case 10:
System.out.println("x=10");
break;

then it wouldn't compile, as the next case would also be 10, as y is 10.

In a more likely scenario those values would be passed in,
as I suggested when I mentioned hunting down the callers to
see what values they're passing in, and then the compiler
can't help prevent this. But just to be clear, I'll remove
the hardcoding, and since the labels are variables (as the
OP was originally requesting), I'll pass those in too, and
also I'll change each println so I don't get accused of
printing non meaningful messages:

public void dispatch( x, A, B )
{
switch( x )
{
case A:
System.out.println("case A executed");
break;
case B:
System.out.println("case B executed");
break;
default:
System.out.println("neither case A or B executed");
break;
}
}
On the other hand, if you intended the comparison to be made with the
runtime value of x, you perhaps should give an example with meaningful
results.
how is your example with calls to a(), b(), c() more
meaningful than calls to println?
In Java, the fall through implies an ordering to the case labels. This
ordering would be such that the mapping of a non-constant case to a
cascade of 'if's would be the same as that of a const case; ie:
why are you bringing fall-through into the discussion? I'm
trying to make the point that indeterminant code can be
written using the OPs request for labels with variable
values. Adding fall-through to the discussion doesn't seem
to clear up anything. Are you suggesting that fall-through
somehow fixes whatever issues I have with the original
request for variable-valued labels?
switch (X) {
case A:
default:
a();
break;
case B:
b();
case C:
c();
}
To run with your example, what happens in this code when A
and B, which I assume are variables, are equal? The
compiler won't know, since they're run-time values, so it
won't complain. So no-one can predict in the case where
X==A and X==B whether a() will be called, or b() will be
called, but clearly in this example, both cannot be called.
Some believe case A should be executed because it comes
first in the list, but I think that would lead to some
obscure coding bugs when someone knows that X==B, yet b()
never get's called (because for some reason A always equals
B, so a() always gets called instead.)
would be equivalent to

switch_1:
do {
do {
if (X==A) continue;
switch_2:
do {
do {
if (X==B) {
b();
continue;
}

if (X==C) {
continue;
}
break switch_2;
} while (false);
} while (false);
c();
break switch_1;
} while (false);
a();
} while (false);

I couldn't make much out of that example, wrapping a
do{}while(false) inside another do{}while(false), beats me
what you're trying to illustrate with that. Maybe that
anyone can write senselessly nested code?
Once such a construct has well defined semantics, the question is trival.
That's the crux of it "well defined semantics". The labels
with variable values don't have any well defined semantics.
No matter how much you wish they did, they don't.
The bugs would be no worse that those in
<snip>
Just because someone can come up with thousands of bug prone
examples of code, doesn't mean we should come up with new
ways to write buggy code.
in the example
if (x==x) {
foo();
} else {
bar();
}

how would you ever get bar to execute? Does this mean that if statements
are inherently bug prone and a maintainance nightmare?
No, because the semantics are clear, because foo() gets
called in all cases, and bar() never gets called. In the
variable labels case, the semantics are not clear, and
that's the trouble with it.
In terms of 'what behaviour the compiler lets you do', we already have
contructs of this power, which erm, just don't look as nice.
You gave the perfect example with that do{}while(false)
stuff above.
Rick
 
T

Tim Tyler

: Miguel De Anda wrote:

:>>doesn't it seem obvious that with your idea you could code
:>>this indeterminant bit of code?
:>>
:>> int x = 10, y = 10;
:>> switch(x)
:>>{
:>> case x:
:>> System.out.println("x=10");
:>> break;
:>> case y:
:>> System.out.println("y=10");
:>> break;
:>> default:
:>> System.out.println("x!=10 and y!=10");
:>> break;
:>>}
:>>
:>>Hmmm, where would it go, to case x or case y?
:>
:> Why wouldn't it just go to the first case?
:
: It could go to case x, but can you imagine the maintenance
: headaches you're going to have when some new person comes
: along and wants to add another case to this? What's he/she
: going to do, hunt down all the callers of the module to see
: who's passing in which values and then try to predict where
: to place the new case label? This poor person will never
: know if the new code is going to execute unless the new case
: is placed at the top to pre-empt all the other labels. How
: bug prone is that? [...]

It's no worse than:

if (a) { b(); return; }
if (c) { d(); return; }
if (e) { f(); return; }

....which seems perfectly acceptable to me.

Insisting they are runtime constants wouldn't be so bad.

It is insisting that they are constant integers that is the
biggest issue - IMO.

I hear we will at least be able to switch on enumerations soon ;-)
 

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,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top