Switch statements can help clarity



  • I yelped just now when I ran across the following, a switch block with exactly one case.

     switch (cmd)
     {
         case Acme.CommandType.VerifyResponse:
             if ( ! this.VerifyResponse(cmd.stuff) )
             {
                 throw new AcmeResponseException("Response is not valid");
             }
             break;
      }
    

    I assume the author intended, perhaps still intends to add more cases. But somehow I'm reminded of a spoof about a prolific author, "...the only man who has written more books than he has read."



  • Seems fine to me. If there is only one CommandType, then there only need be one switch case.

    My only criticism is that there is no default handler in case of an unforseen addition to the CommandType enum.



  • @pkmnfrk said:

    Seems fine to me. If there is only one CommandType, then there only need be one switch case.

    My only criticism is that there is no default handler in case of an unforseen addition to the CommandType enum.

    You really think that the switch is more clear than an if statement? The switch & case baggage adds a bunch of clutter for no purpose that I can discern.

    This is beyond the scope of the original post, but in the code the "cmd" enum has many values, and the overly-long function that the example comes from has several switch statements on the same enum.



  • No, if you're switching on an enum the last thing you want is a default case. That way, when you add a new CommandType the compiler directs you to exactly where other changes are needed.



  • @zmafoox said:

    No, if you're switching on an enum the *last* thing you want is a default case. That way, when you add a new CommandType the compiler directs you to exactly where other changes are needed.

    Uh... no it doesn't.


  • Um, back to the switch statement with only a single "case"...

    IIRC, The Art of Readable Code makes the point that if you're doing the same old thing, it helps clarity to do it the same old way. This makes it easy for a reader to pick out and recognize the various parts and pieces.

    In the WTF sample, the one "case" select block boils down to an overly verbose "if" statement; it should have been implemented as a plain old "if" statement.

    There would have been better ways to mark this block as a place for possible expansion (e.g., add a "TBD" coment). And my bet is that the anticipated expansion will never happen; YAGNI, and all that.


  • Considered Harmful

    @Sutherlands said:

    @zmafoox said:

    No, if you're switching on an enum the last thing you want is a default case. That way, when you add a new CommandType the compiler directs you to exactly where other changes are needed.

    Uh... no it doesn't.

    Switching on an enum, the default case should throw an exception and all acceptable values should do something or just break out.



  • @Sutherlands said:

    @zmafoox said:

    No, if you're switching on an enum the last thing you want is a default case. That way, when you add a new CommandType the compiler directs you to exactly where other changes are needed.

    Uh... no it doesn't.

    Please explain as this works in both C & C++. Any language worth it's salt should complain if an enumeration is not handled in a switch statement.



  • @zmafoox said:

    Please explain as this works in both C & C++. Any language worth it's salt should complain if an enumeration is not handled in a switch statement.

    I really wouldn't consider C++ a language that's worth much of anything, but I wouldn't mind seeing this in other languages.



  • @zmafoox said:

    @Sutherlands said:

    @zmafoox said:

    No, if you're switching on an enum the *last* thing you want is a default case. That way, when you add a new CommandType the compiler directs you to exactly where other changes are needed.

    Uh... no it doesn't.

    Please explain as this works in both C & C++. Any language worth it's salt should complain if an enumeration is not handled in a switch statement.

    <FONT color=#0000ff size=2 face=Consolas><FONT color=#0000ff size=2 face=Consolas><FONT color=#0000ff size=2 face=Consolas>

    enum</FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas> MyEnum {First, Second}

    </FONT></FONT><FONT color=#0000ff size=2 face=Consolas><FONT color=#0000ff size=2 face=Consolas><FONT color=#0000ff size=2 face=Consolas>void</FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas> Main()</FONT></FONT></FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas>

    {

    Print(MyEnum.First);

    Print(MyEnum.Second);

    }

    </FONT></FONT><FONT color=#0000ff size=2 face=Consolas><FONT color=#0000ff size=2 face=Consolas><FONT color=#0000ff size=2 face=Consolas>void</FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas> Print(MyEnum e)</FONT></FONT></FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas>

    {

    </FONT></FONT><FONT color=#0000ff size=2 face=Consolas><FONT color=#0000ff size=2 face=Consolas><FONT color=#0000ff size=2 face=Consolas>switch</FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas>(e)</FONT></FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas>

    {

    </FONT></FONT><FONT color=#0000ff size=2 face=Consolas><FONT color=#0000ff size=2 face=Consolas><FONT color=#0000ff size=2 face=Consolas>case</FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas> MyEnum.First:</FONT></FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas>

    System.Console.WriteLine(</FONT></FONT><FONT color=#dc1414 size=2 face=Consolas><FONT color=#dc1414 size=2 face=Consolas><FONT color=#dc1414 size=2 face=Consolas>"First"</FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas>);

    </FONT></FONT><FONT color=#0000ff size=2 face=Consolas><FONT color=#0000ff size=2 face=Consolas><FONT color=#0000ff size=2 face=Consolas>break</FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas>;</FONT></FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas>

    }

    System.Console.WriteLine(</FONT></FONT><FONT color=#dc1414 size=2 face=Consolas><FONT color=#dc1414 size=2 face=Consolas><FONT color=#dc1414 size=2 face=Consolas>"End"</FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas>);</FONT></FONT></FONT></FONT><FONT size=2 face=Consolas><FONT size=2 face=Consolas>

    }

    Results:

    First
    End
    End
    </FONT></FONT><FONT color=#008000 size=2 face=Consolas><FONT color=#008000 size=2 face=Consolas><FONT color=#008000 size=2 face=Consolas></FONT></FONT></FONT>


  • @Sutherlands said:

    @zmafoox said:

    @Sutherlands said:

    @zmafoox said:

    No, if you're switching on an enum the last thing you want is a default case. That way, when you add a new CommandType the compiler directs you to exactly where other changes are needed.

    Uh... no it doesn't.

    Please explain as this works in both C & C++. Any language worth it's salt should complain if an enumeration is not handled in a switch statement.

    <font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas"> </font></font></font>

    <font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas">enum</font></font></font><font size="2" face="Consolas"><font size="2" face="Consolas"> MyEnum {First, Second}</font></font>

    <font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas">void</font></font></font><font size="2" face="Consolas"><font size="2" face="Consolas"> Main()</font></font><font size="2" face="Consolas"><font size="2" face="Consolas">

    {

    Print(MyEnum.First);

    Print(MyEnum.Second);

    }

    </font></font><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas">void</font></font></font><font size="2" face="Consolas"><font size="2" face="Consolas"> Print(MyEnum e)</font></font><font size="2" face="Consolas"><font size="2" face="Consolas">

    {

    </font></font><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas">switch</font></font></font><font size="2" face="Consolas"><font size="2" face="Consolas">(e)</font></font><font size="2" face="Consolas"><font size="2" face="Consolas">

    {

    </font></font><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas">case</font></font></font><font size="2" face="Consolas"><font size="2" face="Consolas"> MyEnum.First:</font></font><font size="2" face="Consolas"><font size="2" face="Consolas"> </font></font>

    <font size="2" face="Consolas"><font size="2" face="Consolas">System.Console.WriteLine(</font></font><font color="#dc1414" size="2" face="Consolas"><font color="#dc1414" size="2" face="Consolas"><font color="#dc1414" size="2" face="Consolas">"First"</font></font></font><font size="2" face="Consolas"><font size="2" face="Consolas">);</font></font>

    <font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas"><font color="#0000ff" size="2" face="Consolas">break</font></font></font><font size="2" face="Consolas"><font size="2" face="Consolas">;</font></font><font size="2" face="Consolas"><font size="2" face="Consolas">

    }

    System.Console.WriteLine(</font></font><font color="#dc1414" size="2" face="Consolas"><font color="#dc1414" size="2" face="Consolas"><font color="#dc1414" size="2" face="Consolas">"End"</font></font></font><font size="2" face="Consolas"><font size="2" face="Consolas">);</font></font><font size="2" face="Consolas"><font size="2" face="Consolas">

    }

    Results:

    First
    End
    End
    </font></font><font color="#008000" size="2" face="Consolas"><font color="#008000" size="2" face="Consolas"><font color="#008000" size="2" face="Consolas"></font></font></font>

    Right, but it's a warning in C or C++ to have a switch on an enum and not include paths for all types (apparently this guy has never used a language outside of C++ and C--so sad), so he's saying other languages should do the same. I see value in it, although it should also be able to be explicitly suppressed should you want to.



  • Guys, zmafoox said that the compiler will point you to where you missed addressing it.  It won't.  That's all I'm addressing.



  • I have Eclipse set up to warn on enum switches that don't match against a constant in Java. I usually code such switches in a separate method like this:

    switch (someValue) {
        case CONST1:
            ...
            return ...;
        case CONST2:
        case CONST3:
            ...
            return ...;
    }
    throw new AssertionError(someValue);

    That way, the compiler will catch it, and it will fail at runtime if new constants have been added.



  • @arotenbe said:

    I have Eclipse set up to warn on enum switches that don't match against a constant in Java. I usually code such switches in a separate method like this:

    switch (someValue) {
        case CONST1:
            ...
            return ...;
        case CONST2:
        case CONST3:
            ...
            return ...;
    }
    throw new AssertionError(someValue);

    That way, the compiler will catch it, and it will fail at runtime if new constants have been added.

    This is basically the pattern I was referring to when I suggested having a default. That is all.



  • @morbiuswilters said:

    Right, but it's a warning in C or C++ to have a switch on an enum and not include paths for all types
     

    Which compilers? I don't think I've ever seen this. Perhaps because I always have a default case?



  • @too_many_usernames said:

    @morbiuswilters said:

    Right, but it's a warning in C or C++ to have a switch on an enum and not include paths for all types
     

    Which compilers? I don't think I've ever seen this. Perhaps because I always have a default case?

    Visual Studio 2010 definitely has it.


  • Discourse touched me in a no-no place

    @too_many_usernames said:

    @morbiuswilters said:

    Right, but it's a warning in C or C++ to have a switch on an enum and not include paths for all types
    Which compilers? I don't think I've ever seen this.
    GCC for one can be made to emit a warning on a missing case, but (without scouring The Standards) I'm fairly certain it's not a required diagnostic.

    [pjh@sofa tmp]$ cat switch.c
    #include <stdio.h>
    #include <stdlib.h>
    
    enum foo{
            bar = 2,
            baz = 3,
            qux = 4
    };
    
    int main(void){
            enum foo quux = 2 + (rand()%2);
            switch (quux){
                    case bar: printf("bar\n"); break;
                    case baz: printf("baz\n"); break;
            }
            return 0;
    }
    [pjh@sofa tmp]$ gcc switch.c -o switch
    [pjh@sofa tmp]$ gcc switch.c -o switch -Wswitch
    switch.c: In function ‘main’:
    switch.c:12:2: warning: enumeration value ‘qux’ not handled in switch
    [pjh@sofa tmp]$
    
    Perhaps because I always have a default case?
    -Wswitch-enum will emit a warning even if you have a default in gcc

Log in to reply