Is It Possible To Do The Following In A Switch Statement - C++? - 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;
}

Related

Using switch case for bitwise enums

I have implemented my own typesafe bitwise enum operators following this article: http://blog.bitwigglers.org/using-enum-classes-as-type-safe-bitmasks/
Here is the enum I am talking about:
enum class OutputStream : unsigned int
{
None = 0,
// Using bitshift operator (always one bit set to 1)
Console = 1 << 0,
File = 1 << 1,
Other = 1 << 2
};
In case you wonder, it's for a logging function.
Problem:
I want to use the enum in a switch statement such as
switch(stream)
{
case OutputStream::Console:
//Do this
case OutputStream::File:
//Do that
default:
break;
}
Note that there shouldn't be a break; in between the case statements since more than one case can be true.
However, this doesn't seem to work. More precisely, when I use OutputStream::Console | OutputStream::File neither case is executed.
My only solution to this problem was this awkward looking if statement:
if((stream & OutputStream::Console) != OutputStream::None) { /*Do this*/ }
if((stream & OutputStream::File) != OutputStream::None) { /*Do that*/ }
But for me, this defeats the point of a need enum based solution. What am I doing wrong?
As other said in comments, switch is not the best way, but it is still possible to do:
for (int bit = 1; bit <= (int) OutputStream::LAST; bit <<= 1)
{
switch((OutputStream) (bit & stream))
{
case OutputStream::Console:
//Do this
break;
case OutputStream::File:
//Do that
break;
// etc...
// no default case no case 0!
}
}
So basically you will iterate over all individual bits, for each test if it is present in the stream variable and jump to the appropriate case, or jump nowhere if it is 0.
But in my opinion the individual ifs are better. At least you have better control over in which order are the bits evaluated.

In a switch case statement, it says "duplicate case value" comes up as an error. Anyone know why?

I am working on a rock paper scissors program, but this time the computer chooses rock half the time, scissors a third of the time, and paper only one sixth of the time. The way I did this was I enumerated six possible computer choice values:
enum choicec {rock1, rock2, rock3, scissors1, scissors2, paper};
choicec computer;
But then, after the computer makes its choice, I have to convert these enumerated values to either rock, paper, or scissors. I did this using a switch-case statement:
switch(computer) {
case rock1 || rock2 || rock3:
c = 1;
break;
case scissors1 || scissors2: //ERROR!
c = 3;
break;
case paper:
c = 2;
break;
}
one is rock, two is paper, and three is scissors. However, on the line where I have error written in as a comment, it gives me this error: [Error] duplicate case value.
I'm not sure why.
Any ideas?
I am not sure what you doing, but switch statement should look like this
switch(computer)
{
case rock1:
case rock2:
case rock3:
c = 1;
break;
case scissors1:
case scissors2:
c = 3;
break;
case paper:
c = 2;
break;
}
You can't use || in case branches. Sorry :(
When you use || it does a logical or on them, that says "is rock1 or rock2 or rock3 not a zero?". And the answer is yes, at least one of those is not zero. So rock1 || rock2 || rock3 is true, which is 1. And scissors1 || scissors is also true, which is 1. So you have two case branches for the 1 case.
You should simply use case fallthrough to select multiple conditions:
switch(computer) {
case rock1: case rock2: case rock3:
c = 1;
break;
case scissors1: case scissors2:
c = 3;
break;
case paper:
c = 2;
break;
default:
std::cerr << "INVALID COMPUTER MOVE";
}
Also, I always have a default in my case switches. Sometimes mistakes happen, and we definitely want to know if it doesn't hit any of the case branches. I'm also pretty paranoid about missing else statements, but about half the time it's ok if there's no else.
That switch statement does not do what you think.
Each case defines one value that the value of computer is matched against. Combining several values with logical disjunction to give the value associated with a single case label does not make the corresponding block be entered when the value of computer is equal to any of those values, but rather when it is equal to the result of their logical OR combination. Not very meaningful, indeed.
This is how you could rewrite your switch statement in order to make more sense:
switch(computer) {
case rock1: // Is it rock1?
case rock2: // Or perhaps rock2?
case rock3: // Or maybe rock3?
c = 1; // Well, if it's one of the above, do this...
break;
case scissors1: // OK, it wasn't. So is it scissors1?
case scissors2: // Or scissors2?
c = 3; // If it's one of the above, do this...
break;
case paper: // So is it paper?
c = 2;
break;
default: // Always better to be explicit about this
break;
}
Change it to:
switch(computer) {
case rock1:
case rock2:
case rock3:
c = 1;
break;
case scissors1:
case scissors2:
c = 3;
break;
case paper:
c = 2;
break;
}
rock1 || rock2 || rock3 and scissors1 || scissors2 are both expressions which evaluate to "true", hence the conflict.
The expression used in the switch statement must be integral type ( int, char and enum). In the Switch statement, all the matching case execute until a break statement is reached and Two case labels cannot have the same value.
But in the above case with logical or condition.
At first
case: rock1 || rock2 || rock3:
This will evaluate to 1 and second case scissors1 || scissors2: will also evaluate to 1. This is cause error as said Two case labels cannot have the same value.
This is the reason compiler complains and giving an error:
Compiler Error: duplicate case value
To solve this convert to
switch(computer) {
case rock1:
case rock2:
case rock3:
c = 1;
break;
case scissors1:
case scissors2: //Now will not give any error here...
c = 3;
break;
case paper:
c = 2;
break;
}

Why do case statements only accept constants?

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. :)

c++ cannot appear in a constant-expression| [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I select a range of values in a switch statement?
I've been getting some errors, and I've been searching for some time now, but I have no idea what is the cause of the errors. (I'm quite new to programming.)
Here are the errors I'm getting:
error: 'Essais' cannot appear in a constant-expression| (line 200)
warning: overflow in implicit constant conversion| (line 202)
I have case and cote:
char AfficherCote (int Essais)
{
char Cote;
switch (Essais)
{
(line200) case Essais<=20:
{
(line 202) Cote='Excellent';
return (Cote);
break;
}
case Essais<=40:
{
Cote='Très bon';
return (Cote);
break;
}
case Essais<=60:
{
Cote='Bon';
return (Cote);
break;
}
case Essais<=80:
{
Cote='Moyen';
return (Cote);
break;
}
case Essais<=100:
{
Cote='Muvais';
return (Cote);
break;
}
case Essais>=100:
{
Cote='Très mauvais';
return (Cote);
}
}
}
switch-case only works with constant values(*) (such as 3 or 'a'), not with ranges (such as <=100). You also must not include the variable name in the case statement. Correct syntax would be as follows:
switch (Essais)
{
case 1:
/* ... */
break;
case 2:
/* ... */
break;
default:
/* ... */
}
If you need range tests, use if instead of switch-case:
if (Essais <= 80)
return "Cote";
else if (Essais <= 100)
return "Muvais";
Also note that you can't use single quotation marks ' for strings. Use double quotation marks " instead, and use variables of type std::string (not char) to store strings.
(*) To be precise, the condition given in the case statements must be a constant expression of integral type, enumeration type, or class type convertible to integer or enumeration type (see §6.4.2/2 of the C++ Standard for details).
That's not how switch blocks work. You would need to do something like this instead:
switch (Essais) {
case 20:
...
case 40:
...
case 60:
...
/* etc, etc */
}
Each case compares the value in the switch statement against a specific constant value. If they are equal, that block is executed. In your code, the compiler is complaining because an expression like Essais<=20 is not a constant that it can evaluate at compile time.
Given what you are trying to do, an if ... else if ... else chain would be more appropriate. switch blocks can only test against specific values and can't handle testing ranges, which is what it appears you are trying to do.

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.