www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - A safer switch?

reply bearophile <bearophileHUGS lycos.com> writes:
This is a pruned method from a command-line Java program:

private static void parseCmdLine(String args[]) {
  int i = 0;
  String arg;
  while (i < args.length && args[i].startsWith("-")) {
    arg = args[i++];
    if (arg.equals("-s")) {
      if (i < args.length)
        size = new Integer(args[i++]).intValue();
      else
        throw new Error("-l ...");
    } else if (arg.equals("-m")) {
      printMsgs = true;
    } else if (arg.equals("-p")) {
      printResults = true;
    } else if (arg.equals("-h")) {
      usage();
    }
  }
  if (size == 0) usage();
}


I may translate part of that to D like this:

switch (arg) {
    case "-s":
        if (idx < args.length)
            size = toInt(args[idx++]);
        else
            throw new Exception("...");
        break;
    case "-m":
        printMessages = true;
        break;
    case "-p":
        printResults = true;
        break;
    case "-h":
        showUsage();
    default:
        showUsage();
        throw new Exception("...");
}

But the "-h" case misses a break. Languages like C# have found a way to avoid
(in most cases) that common bug. D may use some like this:

switch (arg) {
    case("-s") {
        try {
          next_arg = iargs.next();
          size = toInt(args[idx++]);
        } catch (...) {
            throw new Exception("...");
        }
    }
    case("-m") {
        printMessages = true;
    }
    case("-p") // just 1 istruction, no {} needed
        printResults = true;
    case("-h"); // semicolon isn't allowed here
        showUsage();
    default { // default case may need some care
        throw new Exception("...");
    }
}

Mixing "case:" and "case()" in the same switch is not allowed, to keep things
tidy.

"break" is implicit. You can add "continue" if you want to step to the
following case. If you put the switch inside a loop, the continue is relative
to the switch and not the loop. So if you want to continue the loop you need a
"continue LABEL;" where the LABEL if before the loop.

Bye,
bearophile
Oct 12 2009
next sibling parent reply Justin Johansson <no spam.com> writes:
bearophile Wrote:

 This is a pruned method from a command-line Java program:
 
 private static void parseCmdLine(String args[]) {
...
 }
 
 
 I may translate part of that to D like this:
 
 switch (arg) {
...
 }
 
 But the "-h" case misses a break. Languages like C# have found a way to avoid
(in most cases) that common bug. D may use some like this:
 ...

(Sorry bearophile, don't mean to steal your thunder but since I'm writing some D1 switch code right now, I may as well strike while the iron is still hot ...) Speaking of switch statements, when switching on an enum type, e.g. enum Color { RED, GREEN, BLUE } void foo( Color color) { switch (color) { case Color.RED: break; case Color.GREEN: break; case Color.BLUE: break; } } Would it be possible for the compiler to infer the declared enum type, in this case Color, making for abbreviation of the enum member names in the case clauses as per the following? void bar( Color color) { switch (color) { case RED: break; case GREEN: break; case BLUE: break; } } Koala hugs, -- Justin Johansson
Oct 12 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Justin Johansson:

 Would it be possible for the compiler to infer the declared enum type, in this
case Color, making for abbreviation of the enum member names in the case
clauses as per the following?
 
 void bar( Color color) {
    switch (color) {
    case RED:
       break;
    case GREEN:
       break;
    case BLUE:
       break;
    }
 }

That's a special case, with the purpose of shortening code a little. So while writing Color.something isn't handy, it safe and it has no special casing. So I don't like your idea a lot. Related: I have compiled your code with D1 with warnings (-w): enum Color { RED, GREEN, BLUE } void foo(Color color) { switch (color) { case Color.RED: break; case Color.GREEN: break; case Color.BLUE: break; } } void main() {} It prints: warning - test.d(8): Error: switch statement has no default warning - test.d(8): Error: statement is not reachable But there's no need for default there because all enum cases are covered. So that warning test has to be improved. Bye, bearophile
Oct 12 2009
parent Jeremie Pelletier <jeremiep gmail.com> writes:
bearophile wrote:
 Justin Johansson:
 
 Would it be possible for the compiler to infer the declared enum type, in this
case Color, making for abbreviation of the enum member names in the case
clauses as per the following?

 void bar( Color color) {
    switch (color) {
    case RED:
       break;
    case GREEN:
       break;
    case BLUE:
       break;
    }
 }

That's a special case, with the purpose of shortening code a little. So while writing Color.something isn't handy, it safe and it has no special casing. So I don't like your idea a lot. Related: I have compiled your code with D1 with warnings (-w): enum Color { RED, GREEN, BLUE } void foo(Color color) { switch (color) { case Color.RED: break; case Color.GREEN: break; case Color.BLUE: break; } } void main() {} It prints: warning - test.d(8): Error: switch statement has no default warning - test.d(8): Error: statement is not reachable But there's no need for default there because all enum cases are covered. So that warning test has to be improved. Bye, bearophile

final switch(color) { ... } :)
Oct 12 2009
prev sibling next sibling parent reply language_fan <foo bar.com.invalid> writes:
Mon, 12 Oct 2009 04:55:07 -0400, Justin Johansson thusly wrote:

 Would it be possible for the compiler to infer the declared enum type,
 in this case Color, making for abbreviation of the enum member names in
 the case clauses [snip]?

Yes. Java does this.
Oct 12 2009
parent Justin Johansson <no spam.com> writes:
language_fan Wrote:

 Mon, 12 Oct 2009 04:55:07 -0400, Justin Johansson thusly wrote:
 
 Would it be possible for the compiler to infer the declared enum type,
 in this case Color, making for abbreviation of the enum member names in
 the case clauses [snip]?

Yes. Java does this.

My question written in haste. Strike "possible for the compiler to infer the declared enum type" and replace with "a desirable feature for the compiler to be able to infer the declared enum type". Naturally it would be possible. bearophile Wrote:
 That's a special case, with the purpose of shortening code a little. So while
writing Color.something isn't handy, it safe and it has no special casing. So I
don't like your idea a lot.

Sorry; my feeling is that if it's not necessary to type the extra verbage, why be compelled to do so, especially in this case when the type of the case targets are tightly bound to the type of the var in the switch(). There is absolutely no loss of type safety that I can see .. though I'm prepared to be corrected. btw. It's been a few months since I've coded in Java but the reason the issue stuck me was that I had initially typed the shorter version in D out of die-hard (Java) habit and then, upon complaint, briefly thought, okay Mr D Compiler, since you insist ...
 Related: I have compiled your code with D1 with warnings (-w):
 enum Color {
     RED,
     GREEN,
     BLUE
 }
 
 void foo(Color color) {
     switch (color) {
         case Color.RED:
             break;
         case Color.GREEN:
             break;
         case Color.BLUE:
             break;
     }
 }
 
 warning - test.d(8): Error: switch statement has no default
 warning - test.d(8): Error: statement is not reachable
 
 But there's no need for default there because all enum cases are covered. So
that warning test has to be improved.

Indeed. -- Justin Johansson
Oct 12 2009
prev sibling parent Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
Justin Johansson wrote:
 Speaking of switch statements, when switching on an enum type, e.g.
 
 enum Color {
    RED,
    GREEN,
    BLUE
 }
 
 void foo( Color color) {
    switch (color) {
    case Color.RED:
       break;
    case Color.GREEN:
       break;
    case Color.BLUE:
       break;
    }
 }
 
 Would it be possible for the compiler to infer the declared enum type, in this
case Color, making for abbreviation of the enum member names in the case
clauses as per the following?
 
 void bar( Color color) {
    switch (color) {
    case RED:
       break;
    case GREEN:
       break;
    case BLUE:
       break;
    }
 }
 
 
 Koala hugs,
 
 -- Justin Johansson
 

I bet if you modified parse.c to translate switch(x){ ... case Exp: ... } to switch(x){ ... static if(is(typeof(x) == enum)){ with(typeof(x)){case Exp:}} else {case Exp:} ... } you could have your cake. Can anyone see any potential problems with this (other than symbol shadowing)? Pipsissewa hugs
Oct 12 2009
prev sibling next sibling parent reply "Vladimir Panteleev" <thecybershadow gmail.com> writes:
On Mon, 12 Oct 2009 11:03:13 +0300, bearophile <bearophileHUGS lycos.com>  
wrote:

 This is a pruned method from a command-line Java program:

(snip) This was discussed before. IIRC, someone suggested to force using a control-flow keyword (break/continue/goto) at the end of a case. Using "continue" to fall through was rejected because it changes its meaning when there's a switch in a loop. You'd use goto <value> to jump to the particular (next) switch case. -- Best regards, Vladimir mailto:thecybershadow gmail.com
Oct 12 2009
parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Mon, Oct 12, 2009 at 5:14 AM, Vladimir Panteleev
<thecybershadow gmail.com> wrote:

 This was discussed before. IIRC, someone suggested to force using a
 control-flow keyword (break/continue/goto) at the end of a case. Using
 "continue" to fall through was rejected because it changes its meaning when
 there's a switch in a loop. You'd use goto <value> to jump to the particular
 (next) switch case.

"goto case;" already exists and jumps to the next case in the switch. So bam, you're already set ;)
Oct 12 2009
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Mon, Oct 12, 2009 at 04:03:13AM -0400, bearophile wrote:
 But the "-h" case misses a break.

This has been discussed to death *multiple times*. Why bring it up again, especially with nothing really new to add to the argument? Here's what I suggest you do to get what you want: make a macro in your editor so whenever you type the word "case" it expands it to "break; case". And expand "default" to "break; default". Then, you'll get the implicit breaks you want without changing the language for the rest of us. -- Adam D. Ruppe http://arsdnet.net
Oct 12 2009
prev sibling parent reply grauzone <none example.net> writes:
bearophile wrote:
 switch (arg) {
     case("-s") {
         try {
           next_arg = iargs.next();
           size = toInt(args[idx++]);
         } catch (...) {
             throw new Exception("...");
         }
     }
     case("-m") {
         printMessages = true;
     }
     case("-p") // just 1 istruction, no {} needed
         printResults = true;
     case("-h"); // semicolon isn't allowed here
         showUsage();
     default { // default case may need some care
         throw new Exception("...");
     }
 }

You can do something like this: void main(string[] args) { auto t = ["-s"[]: { writefln("handle argument -s"); }, "-m": { writefln("handle argument -m"); }]; if (auto pcode = args[1] in t) { (*pcode)(); } else { //default case } } Even the ugly and unneeded case labels are gone! If you don't mind your ulta-modern switch-case to allocate memory when it's used. If you still don't like it, you can do some ugly mixin() and CTFE stuff. Nobody will mind because that's modern D style.
Oct 12 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 bearophile wrote:
 switch (arg) {
     case("-s") {
         try {
           next_arg = iargs.next();
           size = toInt(args[idx++]);
         } catch (...) {
             throw new Exception("...");
         }
     }
     case("-m") {
         printMessages = true;
     }
     case("-p") // just 1 istruction, no {} needed
         printResults = true;
     case("-h"); // semicolon isn't allowed here
         showUsage();
     default { // default case may need some care
         throw new Exception("...");
     }
 }

You can do something like this: void main(string[] args) { auto t = ["-s"[]: { writefln("handle argument -s"); }, "-m": { writefln("handle argument -m"); }]; if (auto pcode = args[1] in t) { (*pcode)(); } else { //default case } } Even the ugly and unneeded case labels are gone! If you don't mind your ulta-modern switch-case to allocate memory when it's used. If you still don't like it, you can do some ugly mixin() and CTFE stuff. Nobody will mind because that's modern D style.

import std.getopt; void main(string[] args) { getopt(args, "s", { writefln("handle argument -s"); }, "-m", { writefln("handle argument -m"); }); ... } Andrei
Oct 12 2009