www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - No more fall through in case statement?

reply davidl <davidl 126.com> writes:
http://pragmatic.oreilly.com/pub/a/oreilly/dotnet/news/programmingCsharp_0801.html

I think I love the trap no.9

-- 
使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Jan 03 2008
next sibling parent reply 0ffh <frank youknow.what.todo.interNETz> writes:
davidl wrote:
 [...]
 I think I love the trap no.9

Looks like a compromise, it still falls through for empty cases. =) To me, a warning on nonempty case fallthrough seems more than enough... regards, frank
Jan 03 2008
next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 1/3/08, 0ffh <frank youknow.what.todo.internetz> wrote:
 To me, a warning on nonempty case fallthrough seems more than enough...

I'm sure you know this already, but: "Warnings are not a defined part of the D Programming Language." (http://digitalmars.com/d/warnings.html) Either it's an error, or it's not. Hey - here's an idea. Why not make case statements have a syntax similar to D's attributes. That is, if you use a colon, execution continutes to end of scope (or break), but if you use curly braces, only the stuff within the braces gets executed. (Just like "public", "private", etc.) For example: case 1: /*...*/ /* falls through */ case 2: /*...*/ break; case 3 { /*...*/ } /* does not fall through */ Existing syntax will still work, so that C++ programmers won't get confused. To get no-fall-through behaviour, you'd have to replace the colon with an opening curly brace (and remember the closing brace). Everyone's happy. (Maybe?)
Jan 03 2008
next sibling parent reply Dan <murpsoft hotmail.com> writes:
Janice Caron Wrote:

 On 1/3/08, 0ffh <frank youknow.what.todo.internetz> wrote:
 To me, a warning on nonempty case fallthrough seems more than enough...

I'm sure you know this already, but: "Warnings are not a defined part of the D Programming Language." (http://digitalmars.com/d/warnings.html) Either it's an error, or it's not. Hey - here's an idea. Why not make case statements have a syntax similar to D's attributes. That is, if you use a colon, execution continutes to end of scope (or break), but if you use curly braces, only the stuff within the braces gets executed. (Just like "public", "private", etc.) For example: case 1: /*...*/ /* falls through */ case 2: /*...*/ break; case 3 { /*...*/ } /* does not fall through */ Existing syntax will still work, so that C++ programmers won't get confused. To get no-fall-through behaviour, you'd have to replace the colon with an opening curly brace (and remember the closing brace). Everyone's happy. (Maybe?)

It's creative, and syntactically coherent. I like it. Even if it breaks my old programs, this one makes sense. : )
Jan 03 2008
parent Mike <vertex gmx.at> writes:
On Thu, 03 Jan 2008 16:43:52 +0100, Steven Schveighoffer  =

<schveiguy yahoo.com> wrote:

 I'm not super excited about there being such a subtle difference with =

 colon (if you automatically put a colon without thinking about it, the=

 the
 break statement is required, meaning a very subtle bug), but other tha=

 that, I like the idea.  Why not make it more obvious?:

 case (x, y, z) // similar to case x: case y: case z:
 {
 }
 /* else case(a, b, c) ? */

 -Steve

I want to add two things: (1) When using () after case, the {} should be optional, like in an if: case (1) foo(); case (2, 3) { foo(); bar(); } (2) continue should be used for fall through behavior: case (1) foo(); case (2, 3) { if (x =3D=3D 3) continue 1; bar(); } We could even get rid of "default" and label the default case as "else" = - = saving a keyword :) -- = Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Jan 03 2008
prev sibling next sibling parent reply 0ffh <frank youknow.what.todo.interNETz> writes:
Janice Caron wrote:
 On 1/3/08, 0ffh <frank youknow.what.todo.internetz> wrote:
 To me, a warning on nonempty case fallthrough seems more than enough...

I'm sure you know this already, but: "Warnings are not a defined part of the D Programming Language." (http://digitalmars.com/d/warnings.html)

Right-ho! I do, but I was thinking of C#, what the article was about. =) Actually, I think that "no warnings" is a mistake, as is "no inline".
 [...]
 
 For example:
 
     case 1:
         /*...*/
         /* falls through */
     case 2:
         /*...*/
        break;
     case 3
     {
         /*...*/
     }
         /* does not fall through */
 
 [...]
 Everyone's happy. (Maybe?)

I like it as it is. If it absolutely has to be changed, why not replace the explicit break with an implicit break and make fall- through explicit? Liek so: case 1: /*...*/ fallthrough; // falls through case 2: /*...*/ // no fallthrough -> implicit break case ... I could live with that as a compromise... regards, frank
Jan 03 2008
parent reply Michiel Helvensteijn <nomail please.com> writes:
 I like it as it is. If it absolutely has to be changed, why not
 replace the explicit break with an implicit break and make fall-
 through explicit? Liek so:
 
    case 1:
      /*...*/
      fallthrough; // falls through
    case 2:
      /*...*/
      // no fallthrough -> implicit break
    case ...
 
 I could live with that as a compromise...

But the cases are labels. You fall through labels. To change this for switches would be inconsistent. Actually, I don't like switch statement syntax at all, really. I don't think they should be labels. They should be { } blocks. -- Michiel
Jan 03 2008
parent 0ffh <frank youknow.what.todo.interNETz> writes:
Michiel Helvensteijn wrote:
 But the cases are labels. You fall through labels. To change this for
 switches would be inconsistent.

You're quite right.
 Actually, I don't like switch statement syntax at all, really. I don't think
 they should be labels. They should be { } blocks.

Well, if it's not labels anymore, the implicit break option couln't break consistency, could it? Anyways, I don't want to go on arguing for a change I'd rather not see implemented, except as a compromise for those who don't like the implicit fallthrough... =) regards, frank
Jan 03 2008
prev sibling parent Mike <vertex gmx.at> writes:
On Thu, 03 Jan 2008 12:03:08 +0100, Janice Caron <caron800 googlemail.com>  
wrote:

 For example:

     case 1:
         /*...*/
         /* falls through */
     case 2:
         /*...*/
        break;
     case 3
     {
         /*...*/
     }
         /* does not fall through */

 Existing syntax will still work, so that C++ programmers won't get
 confused. To get no-fall-through behaviour, you'd have to replace the
 colon with an opening curly brace (and remember the closing brace).
 Everyone's happy. (Maybe?)

I've posted this exact same idea a long time ago. It's just so obviously how it should be. -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Jan 03 2008
prev sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 1/3/08, Dan <murpsoft hotmail.com> wrote:
 It's creative, and syntactically coherent.  I like it.  Even if it breaks my
old programs, this one makes sense.  : )

I don't think it would break any old programs at all (which means, maybe it has a fighting chance of getting accepted?). All old and existing code would behave exactly as before, without change. That's because the new behaviour would only come into effect if you used the new syntax. Specifically: case x: (with a colon) implies the existing C behavior - keep executing code until you reach a break, a continue, or a right-brace. Wheras: case x { /*...*/ } (without a colon, but with a left-brace) would mean to execute only that which was within the braces. This construction will never occur in old code, because in existing switch/case syntax, the colon in mandatory.
Jan 03 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Janice Caron" wrote
 On 1/3/08, Dan wrote:
 It's creative, and syntactically coherent.  I like it.  Even if it breaks 
 my old programs, this one makes sense.  : )

I don't think it would break any old programs at all

I haven't done this in any d programs, but in C++ programs, I used to use braces to create a scope in which to declare a variable. What would happen here: case x: { int y = 2; ... } break; I'm not super excited about there being such a subtle difference with the colon (if you automatically put a colon without thinking about it, then the break statement is required, meaning a very subtle bug), but other than that, I like the idea. Why not make it more obvious?: case (x, y, z) // similar to case x: case y: case z: { } /* else case(a, b, c) ? */ -Steve
Jan 03 2008
parent reply Dan Lewis <murpsoft hotmail.com> writes:
Steven Schveighoffer Wrote:

 "Janice Caron" wrote
 On 1/3/08, Dan wrote:
 It's creative, and syntactically coherent.  I like it.  Even if it breaks 
 my old programs, this one makes sense.  : )

I don't think it would break any old programs at all

I haven't done this in any d programs, but in C++ programs, I used to use braces to create a scope in which to declare a variable. What would happen here: case x: { int y = 2; ... } break; I'm not super excited about there being such a subtle difference with the colon (if you automatically put a colon without thinking about it, then the break statement is required, meaning a very subtle bug), but other than that, I like the idea. Why not make it more obvious?: case (x, y, z) // similar to case x: case y: case z: { } /* else case(a, b, c) ? */ -Steve

I tend to agree with Steve. Perhaps using case in the same manner as if/for/while/switch and the rest is more congruent to other program structures. I also agree that falling through should be explicit. I use it alot, but safe and more common behavior is to not do so. I also like the comma notation; it would probably terse up the giant lexer switch a bit. Regards, Dan
Jan 03 2008
parent Matti Niemenmaa <see_signature for.real.address> writes:
Dan Lewis wrote:
 I also like the comma notation; it would probably terse up the giant lexer
 switch a bit.

case a, b, c: is already valid D. http://www.digitalmars.com/d/1.0/statement.html#SwitchStatement -- E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
Jan 04 2008
prev sibling next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
davidl wrote:
 http://pragmatic.oreilly.com/pub/a/oreilly/dotnet/news/program
ingCsharp_0801.html 
 
 
 I think I love the trap no.9
 

I'm no C# guy but that looks like a nice way to fix C's switch. "goto case 5" looks like something straight out of Walter's playbook, too. But it's been discussed many times now and it doesn't seem likely Walter's going to change it in D. --bb
Jan 03 2008
prev sibling parent reply BC <notmi_emayl_adreznot hotmail.com.remove.not> writes:
On Thu, 03 Jan 2008 10:30:09 -0000, davidl <davidl 126.com> wrote:

 http://pragmatic.oreilly.com/pub/a/oreilly/dotnet/news/programmingCsha=

 I think I love the trap no.9

i had a go at rolling my own, here it is if anyone's interested: //public domain import std.stdio; import std.variant; import std.traits; struct SwitchFrame { Variant val; bool active; bool _break; static SwitchFrame opCall(Variant val, bool active, bool _break) { SwitchFrame result; result.val =3D val; result.active =3D active; result._break =3D _break; return result; } } SwitchFrame[] switchStack; void _continue() //execute next case after this one { switchStack[$ - 1].active =3D true; switchStack[$ - 1]._break =3D false; } void _rematch() //execute the next case that matches { switchStack[$ - 1].active =3D false; switchStack[$ - 1]._break =3D false; } void _break() //don't execute any more cases { switchStack[$ - 1].active =3D false; switchStack[$ - 1]._break =3D true; } struct _range(T) { T min; T max; } _range!(T) range(T)(T min, T max) { _range!(T) result; result.min =3D min; result.max =3D max; return result; } struct rangedg(T) { T min; T max; bool func(T val) { return min <=3D val && val <=3D max; } } void myswitch(T, U)(T switchVal, U block) { Variant v =3D switchVal; switchStack ~=3D SwitchFrame(v, false, false); block(); switchStack.length =3D switchStack.length - 1; } void mycase(T...)(T args) { SwitchFrame* frame =3D &switchStack[$ - 1]; if (frame._break) return; if (!frame.active) { foreach (i, arg; args[0 .. $-1]) { if (typeid(T[i]) =3D=3D frame.val.type && arg =3D=3D = frame.val.get!(T[i])) goto success; static if (is (typeof(*arg) U =3D=3D function)) { //writefln("function"); if (arg(frame.val.get!(U))) goto success; } static if (is (T[i] U =3D=3D delegate)) { //writefln("delegate"); if (arg(frame.val.get!(ParameterTypeTuple!(U)))) goto succe= ss; } static if (is (T[i] U =3D=3D _range!(V), V)) { V switchVal1 =3D frame.val.get!(V); if (arg.min <=3D switchVal1 && switchVal1 <=3D arg.max) got= o = success; } } } else { success: _break();// <- change this to change the default action args[$-1](); } } bool always(int a) { return true; } void main() { myswitch(24, { mycase(10, 11, 12, { writefln("10, 11, 12"); = _continue; }); mycase(24, { writefln("24" ); = _rematch; }); mycase(range(3, 25), { writefln("range" ); = _rematch; }); mycase((int a) { return a > 40; }, { writefln(">40" ); = _rematch; }); mycase(&always, { writefln("always" ); = _rematch; }); }); _range!(int) ra =3D {2,3}; }
Jan 04 2008
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
BC wrote:
 On Thu, 03 Jan 2008 10:30:09 -0000, davidl <davidl 126.com> wrote:
 
 http://pragmatic.oreilly.com/pub/a/oreilly/dotnet/news/program
ingCsharp_0801.html 


 I think I love the trap no.9

i had a go at rolling my own, here it is if anyone's interested: //public domain import std.stdio; import std.variant; import std.traits; struct SwitchFrame { Variant val; bool active; bool _break; static SwitchFrame opCall(Variant val, bool active, bool _break) { SwitchFrame result; result.val = val; result.active = active; result._break = _break; return result; } } SwitchFrame[] switchStack; void _continue() //execute next case after this one { switchStack[$ - 1].active = true; switchStack[$ - 1]._break = false; } void _rematch() //execute the next case that matches { switchStack[$ - 1].active = false; switchStack[$ - 1]._break = false; } void _break() //don't execute any more cases { switchStack[$ - 1].active = false; switchStack[$ - 1]._break = true; } struct _range(T) { T min; T max; } _range!(T) range(T)(T min, T max) { _range!(T) result; result.min = min; result.max = max; return result; } struct rangedg(T) { T min; T max; bool func(T val) { return min <= val && val <= max; } } void myswitch(T, U)(T switchVal, U block) { Variant v = switchVal; switchStack ~= SwitchFrame(v, false, false); block(); switchStack.length = switchStack.length - 1; } void mycase(T...)(T args) { SwitchFrame* frame = &switchStack[$ - 1]; if (frame._break) return; if (!frame.active) { foreach (i, arg; args[0 .. $-1]) { if (typeid(T[i]) == frame.val.type && arg == frame.val.get!(T[i])) goto success; static if (is (typeof(*arg) U == function)) { //writefln("function"); if (arg(frame.val.get!(U))) goto success; } static if (is (T[i] U == delegate)) { //writefln("delegate"); if (arg(frame.val.get!(ParameterTypeTuple!(U)))) goto success; } static if (is (T[i] U == _range!(V), V)) { V switchVal1 = frame.val.get!(V); if (arg.min <= switchVal1 && switchVal1 <= arg.max) goto success; } } } else { success: _break();// <- change this to change the default action args[$-1](); } } bool always(int a) { return true; } void main() { myswitch(24, { mycase(10, 11, 12, { writefln("10, 11, 12"); _continue; }); mycase(24, { writefln("24" ); _rematch; }); mycase(range(3, 25), { writefln("range" ); _rematch; }); mycase((int a) { return a > 40; }, { writefln(">40" ); _rematch; }); mycase(&always, { writefln("always" ); _rematch; }); }); _range!(int) ra = {2,3}; }

Neat, but too many braces. :-(((({(} --bb
Jan 04 2008
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 1/4/08, BC <notmi_emayl_adreznot hotmail.com.remove.not> wrote:
 void main()
 {
     myswitch(24,
     {
        mycase(10, 11, 12,                 { writefln("10, 11, 12");
 _continue; });
        mycase(24,                         { writefln("24"        );
 _rematch;  });
        mycase(range(3, 25),               { writefln("range"     );
 _rematch;  });
        mycase((int a) { return a > 40; }, { writefln(">40"       );
 _rematch;  });
        mycase(&always,                    { writefln("always"    );
 _rematch;  });
     });
     _range!(int) ra = {2,3};
 }

A mighty and noble effort - but I can't help but feel that the following is more readable: if (x==10 || x==11 || x==12) writefln("10,11,12"); if (x==24) writefln(24); if (x>=3 && x<=25) writefln("range"); writefln("always"); The way I see it, the advantage of "switch/case" over "if" is that the compiler might be able to find cool ways to optimize the code (e.g binary search, sparse array lookup, table lookup, whatever). If the compiler can't do that, well then, what's the point of using it at all when "if" is perfectly expressive already?
Jan 04 2008
parent reply BC <notmi_emayl_adreznot hotmail.com.remove.not> writes:
On Fri, 04 Jan 2008 17:26:54 -0000, Janice Caron <caron800 googlemail.co=
m>  =

wrote:

 On 1/4/08, BC <notmi_emayl_adreznot hotmail.com.remove.not> wrote:
 void main()
 {
     myswitch(24,
     {
        mycase(10, 11, 12,                 { writefln("10, 11, 12");
 _continue; });
        mycase(24,                         { writefln("24"        );
 _rematch;  });
        mycase(range(3, 25),               { writefln("range"     );
 _rematch;  });
        mycase((int a) { return a > 40; }, { writefln(">40"       );
 _rematch;  });
        mycase(&always,                    { writefln("always"    );
 _rematch;  });
     });
     _range!(int) ra =3D {2,3};
 }

A mighty and noble effort - but I can't help but feel that the following is more readable: if (x=3D=3D10 || x=3D=3D11 || x=3D=3D12) writefln("10,11,12"); if (x=3D=3D24) writefln(24); if (x>=3D3 && x<=3D25) writefln("range"); writefln("always");

 The way I see it, the advantage of "switch/case" over "if" is that the=

 compiler might be able to find cool ways to optimize the code (e.g
 binary search, sparse array lookup, table lookup, whatever). If the
 compiler can't do that, well then, what's the point of using it at all=

 when "if" is perfectly expressive already?

Do you know if the compilers actually do this?
Jan 05 2008
next sibling parent Christopher Wright <dhasenan gmail.com> writes:
BC wrote:
 On Fri, 04 Jan 2008 17:26:54 -0000, Janice Caron 
 <caron800 googlemail.com> wrote:
 The way I see it, the advantage of "switch/case" over "if" is that the
 compiler might be able to find cool ways to optimize the code (e.g
 binary search, sparse array lookup, table lookup, whatever). If the
 compiler can't do that, well then, what's the point of using it at all
 when "if" is perfectly expressive already?

Do you know if the compilers actually do this?

I've heard that javac uses table lookups for switch statements.
Jan 05 2008
prev sibling next sibling parent 0ffh <frank youknow.what.todo.interNETz> writes:
Janice Caron wrote:
 On 1/5/08, BC <notmi_emayl_adreznot hotmail.com.remove.not> wrote:
 The way I see it, the advantage of "switch/case" over "if" is that the 
 compiler might be able to find cool ways to optimize the code (e.g 
 binary search, sparse array lookup, table lookup, whatever). If the 
 compiler can't do that, well then, what's the point of using it at all 
 when "if" is perfectly expressive already?

Do you know if the compilers actually do this?

Me? Hell no! I don't write compilers. [...] I do know the answer for at least one ancient compiler of old. [...] But that was then and this is now and things are bound to be different.

FWIW I totally agree with you that there is virtual certainty that some kind of optimisation (jump table, binary search tree, etc.) is actually used in each of DMD and GDC and more or less any optimising compiler. Whatever the target machine is, those are bound to be faster than a linear chain of if-else-ifs (except for extreme cases). regards, frank
Jan 05 2008
prev sibling parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Sat, 05 Jan 2008 11:50:33 -0000, Janice Caron <caron800 googlemail.com>  
wrote:

 On 1/5/08, BC <notmi_emayl_adreznot hotmail.com.remove.not> wrote:
 The way I see it, the advantage of "switch/case" over "if" is that the
 compiler might be able to find cool ways to optimize the code (e.g
 binary search, sparse array lookup, table lookup, whatever). If the
 compiler can't do that, well then, what's the point of using it at all
 when "if" is perfectly expressive already?

Do you know if the compilers actually do this?

Me? Hell no! I don't write compilers. But Walter may be able to answer that question for D. I do know the answer for at least one ancient compiler of old. I know that back in the days of AmigaDOS, the Amiga compiler used a choice of two strategies. It would create a simple O(1) lookup table if all the values were contiguous, or nearly contiguous, or an O(log(N)) binary search otherwise. But that was then and this is now and things are bound to be different.

SAS C++? them were the days...
Jan 05 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 1/5/08, BC <notmi_emayl_adreznot hotmail.com.remove.not> wrote:
 The way I see it, the advantage of "switch/case" over "if" is that the
 compiler might be able to find cool ways to optimize the code (e.g
 binary search, sparse array lookup, table lookup, whatever). If the
 compiler can't do that, well then, what's the point of using it at all
 when "if" is perfectly expressive already?

Do you know if the compilers actually do this?

Me? Hell no! I don't write compilers. But Walter may be able to answer that question for D. I do know the answer for at least one ancient compiler of old. I know that back in the days of AmigaDOS, the Amiga compiler used a choice of two strategies. It would create a simple O(1) lookup table if all the values were contiguous, or nearly contiguous, or an O(log(N)) binary search otherwise. But that was then and this is now and things are bound to be different.
Jan 05 2008