www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Understanding switch + foreach

reply Matej Nanut <matejnanut gmail.com> writes:
Hello,

I don't understand why so many break statements are needed in this construct:

    immutable key = 3;
    switch (key)
    {
        foreach (c; TypeTuple!(1, 2, 3, 4, 5))
        {
            case c: "Found %s!".writefln(c);
                    break;
        }
        break; // Default always gets executed without this break.
        default:
            "Not found %s :(".writefln(key);
            break;
    }

One after each case and another one after the foreach.

Thanks,
Matej
Apr 07 2014
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Matej Nanut:

         break; // Default always gets executed without this 
 break.
On default compile the D code with warnings active. Bye, bearophile
Apr 07 2014
next sibling parent Matej Nanut <matejnanut gmail.com> writes:
On 8 April 2014 02:30, bearophile <bearophileHUGS lycos.com> wrote:
 On default compile the D code with warnings active.
I'm not sure what you mean? I have the -w and -wi flags always enabled and I don't get any warnings. I'm using DMD 2.065.
Apr 08 2014
prev sibling parent Matej Nanut <matejnanut gmail.com> writes:
Well, I'm still confused by this. I also noticed that the compiler
doesn't complain if I omit the break statements in the generated
switch, but complains normally if I write it out like so:

```
    switch (key)
    {
        case 1: "Found 1!".writefln();
                break;
        case 2: "Found 2!".writefln();
                break;
        case 3: "Found 3!".writefln();
                break;
        case 4: "Found 4!".writefln();
                break;
        case 5: "Found 5!".writefln();
                break;
        default:
                "Not found %s :(".writefln(key);
                break;
    }
```
Apr 16 2014
prev sibling next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
Firest, complete code to save others' time:

import std.stdio;
import std.typetuple;

void main()
{
     immutable key = 3;
     switch (key)
     {
         foreach (c; TypeTuple!(1, 2, 3, 4, 5))
         {
             case c: "Found %s!".writefln(c);
                     break;
         }
         break; // Default always gets executed without this break.
         default:
             "Not found %s :(".writefln(key);
             break;
     }
}

On 04/07/2014 03:30 PM, Matej Nanut wrote:

 I don't understand why so many break statements are needed in this 
construct:
      immutable key = 3;
      switch (key)
      {
          foreach (c; TypeTuple!(1, 2, 3, 4, 5))
Ok, that's a compile-time foreach.
          {
              case c: "Found %s!".writefln(c);
                      break;
That's interesting. Since a compile-time foreach is expanded at compile time, what happens to a break statement in it? Do we break out of the foreach statement or do we insert a break statement? Apparently, the break inside foreach belongs to the switch-case.
          }
          break; // Default always gets executed without this break.
          default:
I think needing that break is a bug. Meanwhile, moving the default block before the foreach seems to be a workaround.
              "Not found %s :(".writefln(key);
              break;
      }

 One after each case and another one after the foreach.

 Thanks,
 Matej
Ali
Apr 07 2014
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 07 Apr 2014 18:30:30 -0400, Matej Nanut <matejnanut gmail.com>  
wrote:

 Hello,

 I don't understand why so many break statements are needed in this  
 construct:

     immutable key = 3;
     switch (key)
     {
         foreach (c; TypeTuple!(1, 2, 3, 4, 5))
         {
             case c: "Found %s!".writefln(c);
                     break;
         }
         break; // Default always gets executed without this break.
         default:
             "Not found %s :(".writefln(key);
             break;
     }

 One after each case and another one after the foreach.
First, let me say this is a cool usage of compile-time foreach, I had never thought of that. Second, I think the issue is likely a bug with the break exiting the wrong scope. You may be able to fix it by labeling the switch scope. For example: theswitch: switch(key) { ... break theswitch; // should exit the switch. } I have not tested it. -Steve
Apr 08 2014
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 04/08/2014 05:14 PM, Steven Schveighoffer wrote:
 On Mon, 07 Apr 2014 18:30:30 -0400, Matej Nanut <matejnanut gmail.com>
 wrote:

 Hello,

 I don't understand why so many break statements are needed in this
 construct:

     immutable key = 3;
     switch (key)
     {
         foreach (c; TypeTuple!(1, 2, 3, 4, 5))
         {
             case c: "Found %s!".writefln(c);
                     break;
         }
         break; // Default always gets executed without this break.
         default:
             "Not found %s :(".writefln(key);
             break;
     }

 One after each case and another one after the foreach.
First, let me say this is a cool usage of compile-time foreach, I had never thought of that. ...
I do this quite often.
 Second, I think the issue is likely a bug with the break exiting the
 wrong scope.
No, this is expected behaviour. break and continue work in any foreach statement. break always breaks the innermost breakable statement. (In particular, it does not pair up with case statements.)
 You may be able to fix it by labeling the switch scope.

 For example:

 theswitch:
     switch(key)
     {

        ...
        break theswitch; // should exit the switch.
     }

 I have not tested it.

 -Steve
Yes, this works.
Apr 17 2014
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 17 Apr 2014 06:54:39 -0400, Timon Gehr <timon.gehr gmx.ch> wrote:

 On 04/08/2014 05:14 PM, Steven Schveighoffer wrote:
 On Mon, 07 Apr 2014 18:30:30 -0400, Matej Nanut <matejnanut gmail.com>
 wrote:

 Hello,

 I don't understand why so many break statements are needed in this
 construct:

     immutable key = 3;
     switch (key)
     {
         foreach (c; TypeTuple!(1, 2, 3, 4, 5))
         {
             case c: "Found %s!".writefln(c);
                     break;
         }
         break; // Default always gets executed without this break.
         default:
             "Not found %s :(".writefln(key);
             break;
     }

 One after each case and another one after the foreach.
First, let me say this is a cool usage of compile-time foreach, I had never thought of that. ...
I do this quite often.
You get a gold star then ;)
 Second, I think the issue is likely a bug with the break exiting the
 wrong scope.
No, this is expected behaviour. break and continue work in any foreach statement. break always breaks the innermost breakable statement. (In particular, it does not pair up with case statements.)
But should a foreach over a tuple a breakable statement? Basically, the above seems to me it should be equivalent to: case 1: writefln("Found %s!", 1); break; case 2: writefln("Found %s!", 2); break; ... The foreach should be gone once the foreach is executed at compile-time. If the break breaks the foreach, why isn't just case 1 produced? That would be an actual break in the foreach, no? -Steve
Apr 17 2014
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 04/17/2014 03:15 PM, Steven Schveighoffer wrote:

 But should a foreach over a tuple a breakable statement?
Well, it is a foreach statement. It is on the other hand not too clear what to do about 'static foreach', but I am leaning towards banning non-labelled break and continue inside it.
 Basically, the above seems to me it should be equivalent to:

 case 1:
     writefln("Found %s!", 1);
     break;
 case 2:
     writefln("Found %s!", 2);
     break;
 ...

 The foreach should be gone once the foreach is executed at compile-time.
 ...
So are the break statements. The lowering is more along the lines of: { case 1: writefln("Found %s!", 1); goto Lbreakforeach; case 2: writefln("Found %s!", 2); goto Lbreakforeach; } Lbreakforeach:;
 If the break breaks the foreach, why isn't just case 1 produced? That
 would be an actual break in the foreach, no?

 -Steve
No. You don't know the dynamic behaviour of the code at runtime just by unrolling the foreach body. import std.stdio; alias Seq(T...)=T; void main(){ int x,y,z; readf("%d %d %d",&x,&y,&z); alias a=Seq!(x,y,z); auto b=[x,y,z]; foreach(v;a){ if(v==2) break; writeln(v); } foreach(v;b){ if(v==2) break; writeln(v); } }
Apr 17 2014
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 17 Apr 2014 10:26:01 -0400, Timon Gehr <timon.gehr gmx.ch> wrote:

 On 04/17/2014 03:15 PM, Steven Schveighoffer wrote:
 If the break breaks the foreach, why isn't just case 1 produced? That
 would be an actual break in the foreach, no?
No. You don't know the dynamic behaviour of the code at runtime just by unrolling the foreach body.
I guess that makes sense. Even though the foreach must be unrolled at compile time, the actual code is executed at runtime. Thanks. -Steve
Apr 17 2014
prev sibling parent reply Matej Nanut <matejnanut gmail.com> writes:
The expansion with gotos explains the behaviour nicely! Cool.

The error about fall-through is still missing though?

Also, I'm sorry for my sparse and perhaps erratic replies. For some
reason, most of the messages in this thread are missing from my Inbox.
The only reply I saw was the first one by bearophile. I don't know
what's going on.
Apr 17 2014
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 04/17/2014 08:04 PM, Matej Nanut wrote:
 The expansion with gotos explains the behaviour nicely! Cool.

 The error about fall-through is still missing though?
Good point, this error should probably be triggered. I guess the problem is roughly that indeed every case statement in the code is terminated by an explicit control flow construct and this is all that is checked for.
Apr 17 2014