Video Coming Soon...
13: switch-statements
In the previous exercise you learned the concept of "jumps" or goto
in C++ and how that relates to the if-statement
. There's another structure in C++ called a switch-statement
that works similarily to an if-statement
, but is better for fast selection based on a simple input. I predict you won't use switch
too much, but there are times when it's superior to if-statements
.
WARNING The
switch
is filled to the brim with footguns. There's all kinds of things that can go wrong, and it can also do many surprising things. When you use it, be sure to follow my advice at the end on how to write footgun resistantswitch
code.
switch
Structure
Here is a basic switch
with each element you can use:
switch(SELECTOR) {
case OPTION:
// code here
break;
default:
// no option matches
}
Here's the breakdown:
- You start a switch with
switch(SELECTOR) {
. - The
SELECTOR
is anything that becomes anint
, so achar
will also work. If you try to use a boolean test like in andif-statement
then it won't work right. - You then need
case OPTION:
to show where the code should jump whenSELECTOR == OPTION
. You can have as many of these as you need to cover the possible options for theSELECTOR
. - You then place your code under this, as shown with
// code here
. - Once your code is finished--usually before the enext
case
ordefault
--you'll need to write abreak
to exit out of the switch. I'll talk about this more later. - You can also add a
default:
label which will run when no othercase
matches. I typically use this to log an error just in case I missed a possiblecase
option. - Finally you end the
switch
with}
just like anif-statement
.
You can think of a switch
as a "controlled group of goto
jumps". Instead of manually calculating a number and jumping to the right line, you use a switch
. You give the switch a number and a set of case
labels, then it does all the work of jumping to the correct location.
Enumerations
Imagine I want to write a switch that works with the two numbers 120
and 54
. Let's say they're from some device that uses those to indicate failure states. I could write my switch
like this:
switch(failure) {
case 120:
break;
case 54:
break;
default:
break;
}
The problem is, what does 120 or 54 mean? In programming we call this a "magic number," and these are typically bad because you can't figure out what a magic number represents by just looking at it. I can figure out it's a failure code of some kind because of failure
, but that's it.
What you need is a way to label these numbers in your code so they're easy to understand. One way is to create two variables, but the better way is to use an enum
, or "enumeration."
An enum looks like this:
enum FailureCode {
BAD_FAIL=120,
MEDIUM_FAIL=54
};
You simply write enum NAME {
to give the enum a name (in this case FailureCode
), then write out the names and values for each one. You don't have to uppercase the names, but it's a standard everyone follows so we can know this is a constant that's probably in an enum
.
You also don't have to give each name a value. You can let the compiler choose like this:
enum FailureCode {
BAD_FAIL, MEDIUM_FAIL
};
In this case the compiler will give each name a value starting at 0
. In our situation though, we need to explicitly set BAD_FAIL=120
and MEDIUM_FAIL=54
to solve the problem.
You then use the enum FailureCode
in the switch
like this:
switch(failure) {
case BAD_FAIL:
break;
case MEDIUM_FAIL:
break;
default:
break;
}
Now it's more clear what those numbers mean, and it's easier to change them later if they're wrong. Let's say you find out that BAD_FAIL
should be 134
instead of 120
. You just change the value in the enum
and recompile. Without the enum
you have to search for 120
and change only the ones related to failure codes.
NOTE You can also write the
switch
so that it's explicit withcase FailureCode::BAD_FAIL:
which will make sure that you're usingBAD_FAIL
from theenum FalureCode
explicitly. For now just use the names without the explicit version until you get more experienced, or if you find you need it.
The Code
I'll now show you a simple piece of code that uses both kinds of switch
statements to do something useless. As usual, get this code working before trying to break it.
View Source file ex13.cpp Only
#include <iostream>
#include <fmt/core.h>
using namespace std;
using namespace fmt;
enum color { RED, GREEN, BLUE};
int main() {
int which_door = 4;
switch(which_door) {
case 1:
println("DOOR #1");
break;
case 2:
println("DOOR #2");
// fallthrough
case 3:
println("DOOR #3");
break;
case 4:
println("DOOR #4");
break;
default:
println("BAD DOOR YOU DIED!");
}
color what_color = RED;
switch(what_color) {
case color::RED:
println("COLOR IS RED");
break;
case color::BLUE:
println("COLOR IS BLUE");
break;
case color::GREEN:
println("COLOR IS GREEN");
break;
default:
// how can you hit this?
println("BAD COLOR");
}
return 0;
}
There's nothing in this code that you don't already know, but if you're not sure about something then look it up on cppreference.com or in previous exercises of this book.
Breaking It
There's many ways to break a switch
statement:
- Try writing a switch that does use a boolean test to see what happens.
- What happens if you leave off the
break
? - What happens if you don't have
default
but give theswitch
an unexpected value? - What if the
switch
is given anint
but all of thecase
are using anenum
? What can happen? - How about the inverse? Your
switch
is anenum
but all thecase
blocks use a number? Is it an error? Should it be? Why?
Bullet Proofing
There's a few things I do to make a switch
statement more bulletproof:
I always include a default
unless I explicitly do not want to handle all the possible cases. Even then I may add it but put a comment saying I'm ignoring possible values. If you have a default
but don't know what to put there, then try logging it as an error. Later you'll learn about exceptions which will abort your code when you have a bad default.
If I do need to have a case
that falls through then I label it with // fallthrough
. The only time I'll not do this is if it's the common pattern of a series of case
with no code in them, usually if I'm parsing and want multiple characters to be handled with one case. For example:
switch(input) {
case 'x':
case 'y':
case 'z':
println("end of alphabet");
break;
default:
println("it's another character");
}
This is a very common idiom and obviously each case
is falling through to the next so no need to flag it. In any other situation I'll add an explicit // fallthrough
comment.
Every other case
statement needs a break
, and I try to write the case
and break
together so I don't forget it.
Further Study
- Try adding a variable to your
case
to see what error you get. For example, aftercolor::RED
create a simplestring name = "red";
. - Try to deduce how you might solve this. I'll give you the solution to this in the next exercise, but try think about where you are allowed to make variables, and can you do something to the
case
so you're allowed. - Add more colors to the
enum color
and use them. - Write yourself an email explaining the difference between
switch
andif
. Sending yourself emails is a great way to solve bugs too. Just email yourself a description of your bug and go for a walk. - Go read about "duff's device." It's wild.
Register for Learn C++ the Hard Way
Register to gain access to additional videos which demonstrate each exercise. Videos are priced to cover the cost of hosting.