Why do case statements only accept constants? - c++

What is the reason behind the fact thet switch statements in C++ have to be written with constants?
Let's take a look at the following code:
switch(variable)
{
case 1:
case 2:
case 3:
case 4:
//Code 1
break;
case 5:
case 6:
case 7:
case 8:
//Code 2
break;
default:
//Code 3
break;
}
In other languages, for example PAWN (C-Like scripting language), I could write this code down as such:
switch(variable)
{
case 1 .. 4:
//Code 1
break;
case 5 .. 8:
//Code 2
break;
default:
//Code 3
break;
}
What is the reason behind the fact the C++ switch statement is soo.. from the Stone Age? (Not to mention we can't use variables.)
Even after so many changes and updates over all those years...

There's no technical reason that C switch statements couldn't be updated to use ranges. gcc already has an extension for this.
http://www.n4express.com/blog/?p=1225
There are good reasons for the values to be constant; that allows for all sorts of optimizations such as jump tables.

If you don't mind another lookup, you could generate a table to simplify your case statement:
char the_case (unsigned variable) {
static const char all_cases[] = {
0,
'A', 'A', 'A', 'A',
'B', 'B', 'B', 'B',
};
if (variable < sizeof(all_cases)) return all_cases[variable];
return 0;
}
//...
switch (the_case(variable)) {
case 'A':
//...
break;
case 'B':
//...
break;
default:
//...
break;
}
Alternatively, you can create an unordered_map to function pointers or methods, where the key is your variable type.

The switch was designed for a simple table-lookup, as was the Pascal case. The Pascal case supported ranges, with, as I recall, the same notation as for Pascal bitsets. C could have done likewise, but for whatever reasons, didn't.
And there's just not been sufficient demand for that feature to make it into either standard, C or C++.
Regarding variables or non-integral types for the case labels, that would change the nature of the statement. C and C++ simply do not have a general select statement. But in C++ you can cook it up yourself:
template< class Key >
auto select(
const Key& key,
const map<Key, function<void()>>& actions
)
-> bool
{
const auto it = actions.find( key );
if( it == actions.end() ) { return false; }
it->second(); return true;
}
And then you can write things like
auto main() -> int
{
cout << "Command? ";
const string command = readline();
select( command, map<string, function<void()>>
{
{ "blah", []{ cout << "You said blah!\n"; } },
{ "asd", []{ cout << "You said asd!?!\n"; } }
} );
}
You can easily add a default to that if you want, e.g. by using the or keyword.
Hm, funny I didn't think of that before, but then apparently neither did anyone else. :)

Related

Why does this switch Statement not return anything when switching compilers from G++ to MSVC? [duplicate]

This question already has answers here:
Function with missing return value, behavior at runtime
(4 answers)
Closed 5 years ago.
I was helping a friend with one of his C++ assignments and we found the following code snippet would throw exceptions in MSVC, but when compiling with G++, the exact same code would work fine. The exceptions were return because this function called getValue() wasn't returning anything.
string getValue(int value) {
ostringstream convert;
string rtnValue;
switch (value) {
case 11:
{
rtnValue = "J";
break;
}
case 12:
{
rtnValue = "Q";
break;
}
case 13:
{
rtnValue = "K";
break;
}
case 14:
{
rtnValue = "A";
break;
}
default:
{
//
// if the value is a a number, we assume it is 2..10
//
convert << value; // use a stream to convert the number
rtnValue = convert.str(); // into a string
if (value < 2 || value > 10)
{
rtnValue = "ERROR" + rtnValue + "ERROR";
}
}
return rtnValue;
}
}
This program turns integers into strings. For the numbers 11-14 it uses switch statement (I know this isn't the best implementation but it's an introductory class).
We found that this could easily be solved by adding another return statement at the end.
string getValue(int value) {
ostringstream convert;
string rtnValue;
switch (value) {
case 11:
{
rtnValue = "J";
break;
}
case 12:
{
rtnValue = "Q";
break;
}
case 13:
{
rtnValue = "K";
break;
}
case 14:
{
rtnValue = "A";
break;
}
default:
{
//
// if the value is a a number, we assume it is 2..10
//
convert << value; // use a stream to convert the number
rtnValue = convert.str(); // into a string
if (value < 2 || value > 10)
{
rtnValue = "ERROR" + rtnValue + "ERROR";
}
}
return rtnValue;
}
return rtnValue;
}
And this now fixes it for MSVC (and I assume G++ if I checked).
Why did that fix work? Does MSVC and G++ treat parentheses differently with respect to switch statements?
In the first example, the return rtnValue is in the wrong place, and will only ever work when the default case is hit.
In the second example, you have added the return rtnValue in the correct place (and the other can be safely removed).
As to why it worked on GCC and not on MSVC, I don't know, without the return being in the correct place, it's not valid C++ (not all paths have a return value), so you should have got a compilation error on any C++ compiler.
I would suggest the problem is actually the way the braces {} are being used, and your friend thought that the closing brace of the default case, actually closed the switch statement, but it doesn't.
Also, there is no need to have braces on any of the case statements. Braces CAN be used in this way to introduce scoping (for example, temporary variables for a particular case), but in your example, just leads to confusion.
this is the problem
default:
{
convert << value; // use a stream to convert the number
rtnValue = convert.str(); // into a string
if (value < 2 || value > 10)
{
rtnValue = "ERROR" + rtnValue + "ERROR";
}
}
return rtnValue;
}
your return statement is in the wrong block, i.e , switch block.
what happens is that, when a case is satisfied it breaks out of the switch that is why it didn't return anything (because it is now out of switch statement).
In order to fix it you have to move your return statement to out of the switch statement to the end of the function.
This correction will we equivalent to the second code that you have provided.
But even in the second code remove the inner return statement.
Return value
Your return statement in the first sample applies to the default case only since the execution of the switch block ends with a break statement in every other case.
In a non-default case, you leave the return value of your function uninitialized. MSVC does warn about that while debugging (see https://learn.microsoft.com/en-us/visualstudio/debugger/how-to-use-native-run-time-checks for details) but GCC does not. This problem might be detected during compile time but you cannot rely on that.
The return statement added to the second sample is correct. You can remove the original one which becomes superfluous.
Braces
Notice that the braces inside the switch block are not necessary and introduce confusion here. They would be only useful if you created a local variable just to be used in a single case. Anyway, the braces should be indented more than the braces of the switch block. This part
}
return rtnValue;
}
demonstrates the misleading indentation clearly. The indentation used in the second example is one of the good solutions to this problem.

Control structures inside switch statements

why the code inside the if block executed any way?
switch(v)
{
case a:
break;
...
if(condition)
{
case f:
...
break;
case g:
...
break;
}
...
case z:
...
break;
default:
}
The C++ compiler uses a lookup table or direct branches to the case-statements. Ignoring your the if-statement. Due to the break it is also not reached from case a.
Long answer short you cannot 'turn off' case-statements using this method.
Instead you'd need something like this:
switch(v) {
case a :
break;
//...
case f :
if(condition) {
//...
}
break;
case g :
if(condition) {
//...
}
break
//...
case z :
break;
}
A case label, as the name implies, is an actual label and works very similar to a goto label: the execution thread just jumps to it. It does not matter what structure it is in, unless that structure is another, nested switch statement.
It works the same way as this:
if (v == f)
goto f_label;
if (condition) {
f_label:
// ...
}
The execution thread will jump to the f_label: label regardless of whether condition is true or not. switch labels work the same way.
The case clauses for a switch are quite flexible and you can do some hacks for them. I have seen some people use switch to break out of nested for loops for instance. Still in the example above if v is f or g the switch will just skip the if statement and the code in the case will be executed right after switch.
When program is compiling switch builds some table to jump from one case to another. This jumps somehow ignoring other conditional operations. BTW according to such behavior switch is faster than long if-else blocks.
I think the best answer to how is( inspired from the answer of Nikos C.):
switch(v)
{
case a:
break;
case z:
...
break;
default:
if(condition)
{
switch(v)
{
case f:
...
break;
case g:
...
break;
default:
//former default
}
}
else
//former default
}
Switch jumps to the matched case ignoring all statements in between. You have two ways to accomplish what you intend to do (depending upon the number of cases you have to implement):
Method 1 for more number of cases under the if conditional
if(condition) {
switch(v) {
case f :
....
break;
//...
case g :
....
break;
//...
case z :
break;
}
switch(v) {
case a :
....
break;
//...
}
Method 2 for less cases under the if conditional
switch(v) {
case a :
break;
//...
case f :
if(condition) {
//...
}
break;
case g :
if(condition) {
//...
}
break
//...
case z :
break;
}

Does case-switch work like this?

I came across a case-switch piece of code today and was a bit surprised to see how it worked. The code was:
switch (blah)
{
case a:
break;
case b:
break;
case c:
case d:
case e:
{
/* code here */
}
break;
default :
return;
}
To my surprise in the scenario where the variable was c, the path went inside the "code here" segment. I agree there is no break at the end of the c part of the case switch, but I would have imagined it to go through default instead. When you land at a case blah: line, doesn't it check if your current value matches the particular case and only then let you in the specific segment? Otherwise what's the point of having a case?
This is called case fall-through, and is a desirable behavior. It allows you to share code between cases.
An example of how to use case fall-through behavior:
switch(blah)
{
case a:
function1();
case b:
function2();
case c:
function3();
break;
default:
break;
}
If you enter the switch when blah == a, then you will execute function1(), function2(), and function3().
If you don't want to have this behavior, you can opt out of it by including break statements.
switch(blah)
{
case a:
function1();
break;
case b:
function2();
break;
case c:
function3();
break;
default:
break;
}
The way a switch statement works is that it will (more or less) execute a goto to jump to your case label, and keep running from that point. When the execution hits a break, it leaves the switch block.
That is the correct behavior, and it is referred to as "falling through". This lets you have multiple cases handled by the same code. In advanced situations, you may want to perform some code in one case, then fall through to another case.
Contrived example:
switch(command)
{
case CMD_SAVEAS:
{
this->PromptForFilename();
} // DO NOT BREAK, we still want to save
case CMD_SAVE:
{
this->Save();
} break;
case CMD_CLOSE:
{
this->Close();
} break;
default:
break;
}
This is called a fall-through.
It is exactly doing what you are seeing: several cases is going to execute same piece of code.
It is also convenient in doing extra processing for certain case, and some shared logic:
// psuedo code:
void stopServer() {
switch (serverStatus)
case STARTING:
{
extraCleanUpForStartingServer();
// fall-thru
}
case STARTED:
{
deallocateResources();
serverStatus = STOPPED;
break;
}
case STOPPING:
case STOPPED:
default:
// ignored
break;
}
This is a typical use of fall-through in switch-case. In case of STARTING and STARTED, we need to do deallocateResources and change the status to STOPPED, but STARTING need some extra cleanup. By the above way, you can clearly present the 'common logic' plus extra logic in STARTING.
STOPPED, STOPPING and default are similar, all of them fall thru to default logic (which is ignoring).
It is not always a good way to code like this but if it is well used it can present the logic better.
Luckily for us, C++ doesn't depend on your imagination :-)
Think of the switch labels as "goto" labels, and the switch(blah) simply "goes to" the corresponding label, and then the code just flows from there.
Actually the switch statement works the way you observed. It is designed so that you can combine several cases together until a break is encountered and it acts something like a sieve.
Here is a real-world example from one of my projects:
struct keystore_entry *new_keystore(p_rsd_t rsd, enum keystore_entry_type type, const void *value, size_t size) {
struct keystore_entry *e;
e = rsd_malloc(rsd, sizeof(struct keystore_entry));
if ( !e )
return NULL;
e->type = type;
switch (e->type) {
case KE_DOUBLE:
memcpy(&e->dblval, value, sizeof(double));
break;
case KE_INTEGER:
memcpy(&e->intval, value, sizeof(int));
break;
/* NOTICE HERE */
case KE_STRING:
if ( size == 0 ) {
/* calculate the size if it's zero */
size = strlen((const char *)value);
}
case KE_VOIDPTR:
e->ptr = rsd_malloc(rsd, size);
e->size = size;
memcpy(e->ptr, value, size);
break;
/* TO HERE */
default:
return NULL;
}
return e;
}
The code for KE_STRING and KE_VOIDPTR cases is identical except for the calculation of size in case of string.

Switch statement using or

I'm creating a console app and using a switch statement to create a simple menu system. User input is in the form of a single character that displays on-screen as a capital letter. However, I do want the program to accept both lower- and upper-case characters.
I understand that switch statements are used to compare against constants, but is it possible to do something like the following?
switch(menuChoice) {
case ('q' || 'Q'):
//Some code
break;
case ('s' || 'S'):
//More code
break;
default:
break;
}
If this isn't possible, is there a workaround? I really don't want to repeat code.
This way:
switch(menuChoice) {
case 'q':
case 'Q':
//Some code
break;
case 's':
case 'S':
//More code
break;
default:
}
More on that topic:
http://en.wikipedia.org/wiki/Switch_statement#C.2C_C.2B.2B.2C_Java.2C_PHP.2C_ActionScript.2C_JavaScript
The generally accepted syntax for this is:
switch(menuChoice) {
case 'q':
case 'Q':
//Some code
break;
case 's':
case 'S':
//More code
break;
default:
break;
}
i.e.: Due the lack of a break, program execution cascades into the next block. This is often referred to as "fall through".
That said, you could of course simply normalise the case of the 'menuChoice' variable in this instance via toupper/tolower.
'q' || 'Q' results in bool type result (true) which is promoted to integral type used in switch condition (char) - giving the value 1. If compiler allowed same value (1) to be used in multiple labels, during execution of switch statement menuChoice would be compared to value of 1 in each case. If menuChoice had value 1 then code under the first case label would have been executed.
Therefore suggested answers here use character constant (which is of type char) as integral value in each case label.
Just use tolower(), here's my man:
SYNOPSIS
#include ctype.h
int toupper(int c);
int tolower(int c);
DESCRIPTION
toupper() converts the letter c to upper case, if possible.
tolower() converts the letter c to lower case, if possible.
If c is not an unsigned char value, or EOF, the behavior of these
functions is undefined.
RETURN VALUE
The value returned is that of the converted letter, or c if the
conversion was not possible.
So in your example you can switch() with:
switch(tolower(menuChoice)) {
case('q'):
// ...
break;
case('s'):
// ...
break;
}
Of course you can use both toupper() and tolower(), with capital and non-capital letters.
You could (and for reasons of redability, should) before entering switch statement use tolower fnc on your var.
switch (toupper(choice))
{
case 'Q':...
}
...or tolower.
if you do
case('s' || 'S'):
// some code
default:
// some code
both s and S will be ignored and the default code will run whenever you input these characters. So you could decide to use
case 's':
case 'S':
// some code
or
switch(toupper(choice){
case 'S':
// some code.
toupper will need you to include ctype.h.

Is It Possible To Do The Following In A Switch Statement - C++?

I am a programming student in my second OOP class, and I have a simple question that I have not been able to find the answer to on the internet, if it's out there, I apologize.
My question is this:
Is it possible have Boolean conditions in switch statements?
Example:
switch(userInputtedInt)
{
case >= someNum && <= someOtherNum
break;
// Is this possible?
}
No this is not possible in C++. Switch statements only support integers and characters (they will be replaced by their ASCII values) for matches. If you need a complex boolean condition then you should use an if / else block
As others have said you can't implement this directly as you are trying to do because C++ syntax doesn't allow it. But you can do this:
switch( userInputtedInt )
{
// case 0-3 inclusve
case 0 :
case 1 :
case 2 :
case 3 :
// do something for cases 0, 1, 2 & 3
break;
case 4 :
case 5 :
// do something for cases 4 & 5
break;
}
No, this is usually the purview of the if statement:
if ((userInputtedInt >= someNum) && (userInputtedInt <= someOtherNum)) { ... }
Of course, you can incorporate that into a switch statement:
switch (x) {
case 1:
// handle 1
break;
default:
if ((x >= 2) && (x <= 20)) { ... }
}
It's not possible directly -- a C or C++ switch statement requires that each case is a constant, not a Boolean expression. If you have evenly distributed ranges, you can often get the same effect using integer division though. e.g. if you have inputs from 1 to 100, and want to work with 90-100 as one group, 80-89 as another group, and so on, you can divide your input by 10, and each result will represent a range.
Or you can perhaps do this
switch((userInputtedInt >= someNum) && (userInputtedInt <= someOtherNum))
{
case true:
//do something
break;
case false:
//something else
break;
}
But that's just down-right terrible programming that could be handled with if-else statements.
This isn't possible. The closest you can some, if the values are reasonably close together is
switch(userInputtedInt)
{
case someNum:
case someNum+1:
// ...
case someOtherNum:
break;
}
C++ does not support that.
However, if you are not concerned with writing portable, standard code some compilers support this extended syntax:
switch(userInputtedInt)
{
case someNum...someOtherNum:
break;
}
Those values must be constant.
If you fancy the preprocessor you could write some kind of macro that auto-expands to the number of case statement required. However that would require a lengthly file with pretty much all case statements (ex: #define CASE0 case 0: #define CASE1 case 1: ...)
You shouldn't go there but it's fun to do...for fun! ;)
The standard does not allow for this:
6.4.2 The switch statement [stmt.switch]
[...] Any statement within the switch statement can be labeled with one or more case labels as follows:
case constant-expression :
where the constant-expression shall be an integral constant expression (5.19).
Some C++ compilers still support range notations today, 8 years after this question was originally asked. It surprised me.
I learned Pascal in 2012, Pascal do have range notations.
So it encouraged me to try the similar syntax in C++, then it worked unexpectedly fabulously.
The compiler on my laptop is g++ (GCC) 6.4.0 (from Cygwin project) std=c++17
There is a working example, which I wrote in hurry. repl.it
In addition, the source code is attached as follow:
#include <iostream>
using namespace std;
#define ok(x) cout << "It works in range(" << x << ")" << endl
#define awry cout << "It does\'t work." << endl
int main() {
/*bool a, b, c, d, e, f, g;
switch(true) {
case (a): break; These does not work any more...
case (b and c): break;
}*/
char ch1 = 'b';
switch(ch1) {
case 'a' ... 'f': ok("a..f"); break;
case 'g' ... 'z': ok("g..z"); break;
default: awry;
}
int int1 = 10;
switch(int1) {
case 1 ... 10: ok("1..10"); break;
case 11 ... 20: ok("11..20"); break;
default: awry;
}
return 0;
}