Why can't I have a variable in switch-case statement? [duplicate] - c++

This question already has answers here:
Case expression not constant
(5 answers)
Closed 5 years ago.
Here is my code:
bool Character::keyPress(char c)
{
switch(c)
{
case up_key:
move(0, -1);
break;
case down_key:
move(0, 1);
break;
case left_key:
move(-1, 0);
break;
case right_key:
move(1,0);
break;
default:
return false;
}
return true;
}
And the compiler complains:
error C2051: case expression not constant
error C2051: case expression not constant
error C2051: case expression not constant
error C2051: case expression not constant
In my header file I have:
protected:
char up_key;
char down_key;
char right_key;
char left_key;
I am using Visual C++ 2008.

As the error message states, the case expressions must be constant. The compiler builds this as a very fast look-up table at compile time and it can't do that if there is a possibility that the values could change as the program runs.
If you do need them to be variable, not constant, your best bet is to use if/else statements instead.

Replace this long clumsy code,
switch(c)
{
case up_key:
move(0, -1);
break;
case down_key:
move(0, 1);
break;
case left_key:
move(-1, 0);
break;
case right_key:
move(1,0);
break;
default:
return false;
}
with something like this:
move( (c==right_key) - (c==left_key) , (c==down_key) - (c==up_key) );
You can litterly replace that 17 lines long of code with that much more neat single line of code.

You can't because the language doesn't work that way. For example, what would happen if up_key, down_key, right_key, and left_key were all equal?

Because the switch statement can take only constants, you know when reading the code that the things you're comparing against are all constants. On the other hand, you would use if statements (or some other structure) to compare against variables:
if (c == up_key) {
move(0, -1);
} else if (c == down_key) {
move(0, 1);
} else ...
This provides a distinct difference in structure which can greatly aid those who come after you in reading your code. Imagine if you had to look up every case label to see whether it was a variable or not?

I believe it's because the compiler generates a jump table, with the values hardcoded in, although I may be wrong. The way the tables are generated just doesn't allow for it.

Since other answers have covered why you are getting an error, here is a way to move in one of the four directions in response to a key press: use lookup tables instead of the conditionals/switches.
Setup portion:
std::map<char,pair<int,int> > moves;
moves[up_key] = make_pair(0, -1);
moves[down_key] = make_pair(0, 1);
moves[left_key] = make_pair(-1, 0);
moves[right_key] = make_pair(1, 0);
The function:
bool Character::keyPress(char c) {
if (moves.count(c)) {
pair<int,int> dir = moves[c];
move(dir.first, dir.second);
return true;
} else {
return false;
}
}

//here is the full functional code snippet which can be compiled and run with most of C++
//compiler/link ...console app was demoed but you can apply the code/logic to win32 app...
//if you have any problem, send me email to Samuel_Ni#yahoo.com
#include <iostream.h>
#include <map>
#include <conio.h>
class CkbdHanler{
private:
map<char,pair<int,int> > moves;
protected:
char up_key;
char down_key;
char right_key;
char left_key;
public:
CkbdHanler(char a,char b,char c,char d):up_key(a),
down_key(b),
right_key(c),
left_key(d)
{
moves[up_key] = make_pair(0, -1);
moves[down_key] = make_pair(0, 1);
moves[left_key] = make_pair(-1, 0);
moves[right_key] = make_pair(1, 0);
}
bool keyPress(char c){
if (moves.count(c)) {
pair<int,int> dir = moves[c];
move(dir.first, dir.second);
return true;
} else return false;
}
void move(int i,int j){
cout<<"(i,j)=("<<i<<","<<j<<")"<<endl;
}
};
int main(int argc, char* argv[])
{
CkbdHanler CmyKbdH('u','d','l','r');
cout << "Hello C++... here is a demo of Map to replace switch-case" << endl;
CmyKbdH.keyPress('d');
cout << endl << "Press any key to continue...";
getch();
return 0;
}

Related

how to use enum inside a switch condition

The following code uses a switch with enum. The main program passes the argument correctly to the function, but the correct switch line is not executed. Can you advise why it is not entering the switch conditions?
enum MyEnum {
Enum1 = 1,
Enum2 = 0x0D
};
bool compute(MyEnum code) {
switch(code) {
Enum1: return true;
Enum2: return false;
};
cout << "why here??" << endl; // this line is getting printed for both inputs
return false;
}
int main() {
cout << "compack=" << compute((MyEnum)1) << endl; // printed "0"
cout << "compack=" << compute((MyEnum)13) << endl; // printed "0"
}
I checked the other questions related to switch and enum (eg 3019153), but cant figure out the bug.
You are missing the case keyword:
switch(code) {
case Enum1: return true;
case Enum2: return false;
};
switch(code)
{
case Enum1: return true;
case Enum2: return false;
};
You forgot to write case
switch(code)
{
case Enum1: return true;
case Enum2: return false;
};
A generic switch is like:
switch(var)
{
case val1:
foo();
break;
case val2:
bar();
break;
default:
error();
};
You forgot case there..
switch(code)
{
case Enum1:
//do something
break;
case Enum2:
//do something
break;
};
Okay, so others have answered that you are missing the case keyword. What hasn't been explained, though, is why the original code compiled. That's because without the case keyword, it was treated as a goto label. In fact, this compiles:
switch (i) {
if (j == 3) {
case 1:;
L1:;
} else {
goto L1;
case 2:;
}
}
Note that the j==3 is actually dead code. It can never be executed. For an actual useful application of this, see Duff's device. By the way, compiling with full warnings enabled would have warned you about an unused goto label, at least with g++ and clang++ (-Wall -Wextra -pedantic).

Adding split-screen multiplayer to c++ game

I am coding for the NDS in c++ with libnds, but this question is not NDS-Specific. I currently have a text-based game in which the top screen just displays a logo, and you play on the bottom screen.
So I want to add a type of single-DS multiplayer in which one player plays on the top screen, and the other on the bottom. I dont have a problem with setting up the text engine with both screens, I just need to find a method of efficiently coding in multiplayer. Below I wrote a summary or simplified version of it.
Note: consoleClear() clears the screen and the only spot where the game stops is att the pause function.
//Headers
void display(int x,int y,const char* output))
{
printf("\x1b[%d;%dH%s", y, x,output);
}
void pause(KEYPAD_BITS key) //KEYPAD_BITS is an ENUM for a key on the NDS
{
scanKeys();
while (keysHeld() & key)
{
scanKeys();
swiWaitForVBlank();
}
while (!(keysHeld() & key))
{
scanKeys();
swiWaitForVBlank();
}
return;
}
void pause() //Only used to simplify coding
{
pause(KEY_A);
return;
}
int main(void)
{
//Initializations/Setup
while (1)
{
if (rand()%2==1) //Say Hello
{
if (rand()%3!=1) //To Friend (greater chance of friend than enemy)
{
display(6,7,"Hello Friend!");
display(6,8,"Good greetings to you.");
pause();
consoleClear(); //Clears text
display(6,7,"Would you like to come in?");
pause();
//Normally more complex complex code (such as interactions with inventories) would go here
}
else //To enemy
{
display(6,7,"Hello enemy!");
display(6,8,"I hate you!");
pause();
consoleClear();
display(6,7,"Leave my house right now!!!");
pause();
}
}
else //Say goodbye
{
if (rand()%4==1) //To Friend (lesser chance of friend than enemy)
{
display(6,7,"Goodbye Friend!");
display(6,8,"Good wishes to you.");
pause();
consoleClear();
display(6,7,"I'll see you tomorrow.");
pause();
consoleClear();
display(6,7,"Wait, I forgot to give you this present.");
pause();
}
else //To enemy
{
display(6,7,"Goodbye enemy!");
display(6,8,"I hate you!");
pause();
consoleClear();
display(6,7,"Never come back!!");
pause();
consoleClear();
display(6,7,"Good riddance!"); //I think I spelt that wrong...
pause();
}
}
}
}
I know gotos are confusing and can be considered a bad habit, but I cant think of a better way. My version of integrating multiplayer:
//Headers and same functions
int game(int location)
{
switch (location)
{
case 1: goto one; break;
case 2: goto two; break;
case 3: goto three; break;
case 4: goto four; break;
case 5: goto five; break;
case 6: goto six; break;
case 7: goto seven; break;
case 8: goto eight; break;
case 9: goto nine; break;
case 10: goto ten; break;
default: break;
}
if (rand()%2==1) //Say Hello
{
if (rand()%3!=1) //To Friend (greater chance of friend than enemy)
{
display(6,7,"Hello Friend!");
display(6,8,"Good greetings to you.");
return 1;
one:;
consoleClear(); //Clears text
display(6,7,"Would you like to come in?");
return 2;
two:;
//Normally more complex complex code (such as interactions with inventories) would go here
}
else //To enemy
{
display(6,7,"Hello enemy!");
display(6,8,"I hate you!");
return 3;
three:;
consoleClear();
display(6,7,"Leave my house right now!!!");
return 4;
four:;
}
}
else //Say goodbye
{
if (rand()%4==1) //To Friend (lesser chance of friend than enemy)
{
display(6,7,"Goodbye Friend!");
display(6,8,"Good wishes to you.");
return 5;
five:;
consoleClear();
display(6,7,"I'll see you tomorrow.");
return 6;
six:;
consoleClear();
display(6,7,"Wait, I forgot to give you this present.");
return 7;
seven:;
}
else //To enemy
{
display(6,7,"Goodbye enemy!");
display(6,8,"I hate you!");
return 8;
eight:;
consoleClear();
display(6,7,"Never come back!!");
return 9;
nine:;
consoleClear();
display(6,7,"Good riddance!"); //I think I spelt that wrong...
return 10;
ten:;
}
return -1;
}
}
int main(void)
{
//Initializations/Setup
int location1 = -1, location2 = -1;
location1 = game(location1);
location2 = game(location2);
while (1)
{
scanKeys(); //Whenever checking key state this must be called
if (keysDown() & KEY_A) //A key is used to continue for player1
location1 = game(location1);
if (keysDown() & KEY_DOWN) //Down key is used to continue for player2
location2 = game(location2);
}
}
Aside from this method being a bad practice, in the actual source code, I have hundreds of gotos I would need to add which would be too time consuming.
Any help is appreciated. If anyone has the slightest of a question, or answer, please ask/reply.
Edit: Though it is not preferred to do so, I am willing to rewrite the game from scratch if someone has a method to do so.
Using if-else conditional statements for each case is a simple solution that comes first to mind.
For example:
int game(int i){
if(i == 1){
//first case code here.
}
else if(i == 2){
//second case code here.
}
//....
return 0;
}
The code in each case can even be put in other functions that will be invoked depending on each condition.
This will probably be enough for your case.
A more flexible solution (but much more complex) is a dispatch table.
The idea is to have separate functions with each desired functionality, and put pointers of them in an array. Then, you can call them by indexing the table, using those function pointers. This can be extremely helpful if you have a sequence of executions (function invokes) to be done and you want to set it done easily, or you want to have different results depending on your input, without changing your program.
There is an example below.
This code can be used in C too, if you replace std::cout with printf and iostream with stdio library.
#include <iostream>
using namespace std;
// Arrays start from 0.
// This is used for code
// readability reasons.
#define CASE(X) X-1
typedef void (*chooseCase)();
// Functions to execute each case.
// Here, I am just printing
// different strings.
void case1(){
cout<< "case1" << endl;
}
void case2(){
cout<< "case2" << endl;
}
void case3(){
cout<< "case3" << endl;
}
void case4(){
cout<< "case4" << endl;
}
//Put all the cases in an array.
chooseCase cases[] = {
case1, case2, case3, case4
};
int main()
{
//You can call each scenario
//by hand easily this way:
cases[CASE(1)]();
cout << endl;
//Idea: You can even set in another
// array a sequence of function executions desired.
int casesSequence[] = {
CASE(1), CASE(2), CASE(3), CASE(4),CASE(3),CASE(2),CASE(1)
};
//Execute the functions in the sequence set.
for(int i = 0; i < (sizeof(casesSequence)/sizeof(int)); ++i){
cases[casesSequence[i]]();
}
return 0;
}
This will print at the output:
case1
case1
case2
case3
case4
case3
case2
case1

Using a function call in a case statement label

Can you have a function call as a case statement label. For instance:
char x
switch(x)
{
case isCapital():
capitalcount++;
break;
case isVowel():
vowelcount++;
break;
.
.
.
.
.
}
Is this permitted within C++?
The value in a case label needs to be a constant expression. That is, the answer to your immediate question is: yes, you can call certain functions in a case label. However, not the ones you tried to call. You can have multiple labels refer to one group of statements, though:
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
do_vowels();
break;
I know this doesn't answer your question per se, but you might try coding it like this....
capitalcount += isCapital(x);
vowelcount += isVowel(x);
The boolean return type of the isXXX() functions would get promoted to an int and added to the counts as either 0 (false) or 1 (true).
First of all: in your desired code isCapital and isVowel should be not functions (and not a function call, definitely), but functors -- because to check a value they have to receive it via parameters...
anyway your code is not possible in C++... but can be simulated with a sequence of pairs of functions: predicate + effect. Predicate have to take some parameter and respond with a boolean. Effect will do smth if predicate is true. To simulate break and fallback to next case (i.e. when no break in a case) effect function also have to return a boolean.
Sample code may look like this:
#include <cctype>
#include <functional>
#include <iostream>
#include <vector>
int main(int argc, char* argv[])
{
typedef std::vector<
std::pair<
std::function<bool(char)> // predicate
, std::function<bool()> // effect: return true if `break' required
>
> case_seq_t;
unsigned digits = 0;
unsigned upper = 0;
unsigned lower = 0;
unsigned total = 0;
unsigned other = 0;
case_seq_t switch_seq = {
{
// predicate lambda can be replaced by std::bind
// in this simple case... but need to change param type.
// std::bind(&std::isdigit, std::placeholders::_1)
[](char c) { return std::isdigit(c); }
, [&]() { digits++; return true; }
}
, {
[](char c) { return std::islower(c); }
, [&]() { lower++; return true; }
}
, {
[](char c) { return std::isupper(c); }
, [&]() { upper++; return true; }
}
// `default` case
, {
[](char c) { return true; }
, [&]() { other++; return true; }
}
};
for (int i = 1; i < argc; i++)
for (int pos = 0; argv[i][pos]; pos++)
for (const auto& p : switch_seq)
if (p.first(argv[i][pos]))
if (p.second())
break;
std::cout << "digits=" << digits << std::endl;
std::cout << "upper=" << upper << std::endl;
std::cout << "lower=" << lower << std::endl;
std::cout << "other=" << other << std::endl;
return 0;
}
Not so simple as switch but (IMHO) obvious enough... and maybe, in some real cases, have better flexibility (and probably maintainability) :)

Fixing a weird write to file bug in c++

i got this really wierd problem, i'm writing my results to an output file, i use functions A B and C i activate them in that order, the results in the file is printed in a different order, first from A than from C and after that from B. i just can't understand why the results printed in a different order than the activation order. thanx.
the code :
void Manager::AddCommand(Command* com, ofstream &ResultFile)
{
if (com != NULL)
{
if (com->ValidCommand(ResultFile) == true)
commands.push_back(com);
else
delete com;
}
}
bool Command::ValidCommand(ofstream &Result) const
{
if(func<PrintCityCouncilList || func >HireEmployee){
Result << "Command:Failed activating function - invalid function number\n";
return false;}
if ((func == Command::PrintDepartmentEmployees) || (func == Command::PrintDepartmentExpenses) || (func == Command::PrintDepartmentStatistics)){
if(dept<Employee::Engineering ||dept>Employee::Sanitation )
{
Result << "Command:Failed activating function - invalid department number\n";
return false;
}
}
return true;
}
void Manager::ActivateCommand(Command* com, ofstream &Result)
{
if(com != NULL)
{
switch(com->GetFunction())
{
case (Command::PrintCityCouncilList): pcc->PrintCityCouncilDetails(Result);
break;
case (Command::PrintDepartmentEmployees):ActivatePrintDeprtEmployees(com->GetDepartment(), Result);
break;
case (Command::PrintEmployeeSalary):ActivateEmployeeSalary(com->GetId(), Result);
break;
case (Command::PrintDepartmentExpenses):ActivateDeprtExpenses(com->GetDepartment(), Result);
break;
case (Command::PrintCityCouncilExpenses): pcc->AllExpenses (Result);
break;
case (Command::PrintDepartmentStatistics):ActivateDeprtStatistics(com->GetDepartment(), Result);
break;
case (Command::FireEmployee): pcc->RemoveEmployeeFromCC(NULL,com->GetId(),Result);
break;
case (Command::HireEmployee): pcc->AddEmployeeToCC(com->GetId(),com->GetPrivateName(),com->GetSurName(),com->GetDate(),com->GetAddress(),com->GetDepartment(), com->GetStatus(),com->GetSalary(),com->GetPositionPercent(),com->GetPhoneNum(), Result);
break;
default:Result<<"Manager:Failed Activating command - invalid function"<<endl;
break;
}
}
}
void Manager::ActivateCommandsList(ofstream &Result)
{
Command* tmp = NULL;
if (commands.empty() == false)
{
list<Command*>::iterator iter = commands.begin();
while (iter != commands.end())
{
tmp = (Command*)(*iter);
ActivateCommand(tmp,Result);
iter++;
}
}
}
Ok here's the deal, your code maybe having some difficulties if you are running it in visual stdio. You see, it has some bugs when it tries to optimize your code. Turn optimization off. Also flush your stream, that's done when putting endl in cout and \n in printf. There is another possibility and it is Stack-Corruption or Heap-Corruption. Check to see if your dynamic objects are referenced within their boundaries.
As long as you don't send the code, like my friend just said we should consult the crystall ball.
My guess is that if this is homework, you are not having a threading issue. Have you tried to flush the output stream from A, B, and C? That might solve your problem.

Why can't the switch statement be applied to strings?

Compiling the following code gives the error message: type illegal.
int main()
{
// Compilation error - switch expression of type illegal
switch(std::string("raj"))
{
case"sda":
}
}
You cannot use string in either switch or case. Why? Is there any solution that works nicely to support logic similar to switch on strings?
The reason why has to do with the type system. C/C++ doesn't really support strings as a type. It does support the idea of a constant char array but it doesn't really fully understand the notion of a string.
In order to generate the code for a switch statement the compiler must understand what it means for two values to be equal. For items like ints and enums, this is a trivial bit comparison. But how should the compiler compare 2 string values? Case sensitive, insensitive, culture aware, etc ... Without a full awareness of a string this cannot be accurately answered.
Additionally, C/C++ switch statements are typically generated as branch tables. It's not nearly as easy to generate a branch table for a string style switch.
As mentioned previously, compilers like to build lookup tables that optimize switch statements to near O(1) timing whenever possible. Combine this with the fact that the C++ Language doesn't have a string type - std::string is part of the Standard Library which is not part of the Language per se.
I will offer an alternative that you might want to consider, I've used it in the past to good effect. Instead of switching over the string itself, switch over the result of a hash function that uses the string as input. Your code will be almost as clear as switching over the string if you are using a predetermined set of strings:
enum string_code {
eFred,
eBarney,
eWilma,
eBetty,
...
};
string_code hashit (std::string const& inString) {
if (inString == "Fred") return eFred;
if (inString == "Barney") return eBarney;
...
}
void foo() {
switch (hashit(stringValue)) {
case eFred:
...
case eBarney:
...
}
}
There are a bunch of obvious optimizations that pretty much follow what the C compiler would do with a switch statement... funny how that happens.
C++
constexpr hash function:
constexpr unsigned int hash(const char *s, int off = 0) {
return !s[off] ? 5381 : (hash(s, off+1)*33) ^ s[off];
}
switch( hash(str) ){
case hash("one") : // do something
case hash("two") : // do something
}
Update:
The example above is C++11. There constexpr function must be with single statement. This was relaxed in next C++ versions.
In C++14 and C++17 you can use following hash function:
constexpr uint32_t hash(const char* data, size_t const size) noexcept{
uint32_t hash = 5381;
for(const char *c = data; c < data + size; ++c)
hash = ((hash << 5) + hash) + (unsigned char) *c;
return hash;
}
Also C++17 have std::string_view, so you can use it instead of const char *.
In C++20, you can try using consteval.
C++ 11 update of apparently not #MarmouCorp above but http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4067/Switch-on-Strings-in-C.htm
Uses two maps to convert between the strings and the class enum (better than plain enum because its values are scoped inside it, and reverse lookup for nice error messages).
The use of static in the codeguru code is possible with compiler support for initializer lists which means VS 2013 plus. gcc 4.8.1 was ok with it, not sure how much farther back it would be compatible.
/// <summary>
/// Enum for String values we want to switch on
/// </summary>
enum class TestType
{
SetType,
GetType
};
/// <summary>
/// Map from strings to enum values
/// </summary>
std::map<std::string, TestType> MnCTest::s_mapStringToTestType =
{
{ "setType", TestType::SetType },
{ "getType", TestType::GetType }
};
/// <summary>
/// Map from enum values to strings
/// </summary>
std::map<TestType, std::string> MnCTest::s_mapTestTypeToString
{
{TestType::SetType, "setType"},
{TestType::GetType, "getType"},
};
...
std::string someString = "setType";
TestType testType = s_mapStringToTestType[someString];
switch (testType)
{
case TestType::SetType:
break;
case TestType::GetType:
break;
default:
LogError("Unknown TestType ", s_mapTestTypeToString[testType]);
}
The problem is that for reasons of optimization the switch statement in C++ does not work on anything but primitive types, and you can only compare them with compile time constants.
Presumably the reason for the restriction is that the compiler is able to apply some form of optimization compiling the code down to one cmp instruction and a goto where the address is computed based on the value of the argument at runtime. Since branching and and loops don't play nicely with modern CPUs, this can be an important optimization.
To go around this, I am afraid you will have to resort to if statements.
std::map + C++11 lambdas pattern without enums
unordered_map for the potential amortized O(1): What is the best way to use a HashMap in C++?
#include <functional>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
int main() {
int result;
const std::unordered_map<std::string,std::function<void()>> m{
{"one", [&](){ result = 1; }},
{"two", [&](){ result = 2; }},
{"three", [&](){ result = 3; }},
};
const auto end = m.end();
std::vector<std::string> strings{"one", "two", "three", "foobar"};
for (const auto& s : strings) {
auto it = m.find(s);
if (it != end) {
it->second();
} else {
result = -1;
}
std::cout << s << " " << result << std::endl;
}
}
Output:
one 1
two 2
three 3
foobar -1
Usage inside methods with static
To use this pattern efficiently inside classes, initialize the lambda map statically, or else you pay O(n) every time to build it from scratch.
Here we can get away with the {} initialization of a static method variable: Static variables in class methods , but we could also use the methods described at: static constructors in C++? I need to initialize private static objects
It was necessary to transform the lambda context capture [&] into an argument, or that would have been undefined: const static auto lambda used with capture by reference
Example that produces the same output as above:
#include <functional>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
class RangeSwitch {
public:
void method(std::string key, int &result) {
static const std::unordered_map<std::string,std::function<void(int&)>> m{
{"one", [](int& result){ result = 1; }},
{"two", [](int& result){ result = 2; }},
{"three", [](int& result){ result = 3; }},
};
static const auto end = m.end();
auto it = m.find(key);
if (it != end) {
it->second(result);
} else {
result = -1;
}
}
};
int main() {
RangeSwitch rangeSwitch;
int result;
std::vector<std::string> strings{"one", "two", "three", "foobar"};
for (const auto& s : strings) {
rangeSwitch.method(s, result);
std::cout << s << " " << result << std::endl;
}
}
To add a variation using the simplest container possible (no need for an ordered map)... I wouldn't bother with an enum--just put the container definition immediately before the switch so it'll be easy to see which number represents which case.
This does a hashed lookup in the unordered_map and uses the associated int to drive the switch statement. Should be quite fast. Note that at is used instead of [], as I've made that container const. Using [] can be dangerous--if the string isn't in the map, you'll create a new mapping and may end up with undefined results or a continuously growing map.
Note that the at() function will throw an exception if the string isn't in the map. So you may want to test first using count().
const static std::unordered_map<std::string,int> string_to_case{
{"raj",1},
{"ben",2}
};
switch(string_to_case.at("raj")) {
case 1: // this is the "raj" case
break;
case 2: // this is the "ben" case
break;
}
The version with a test for an undefined string follows:
const static std::unordered_map<std::string,int> string_to_case{
{"raj",1},
{"ben",2}
};
// in C++20, you can replace .count with .contains
switch(string_to_case.count("raj") ? string_to_case.at("raj") : 0) {
case 1: // this is the "raj" case
break;
case 2: // this is the "ben" case
break;
case 0: //this is for the undefined case
}
In C++ and C switches only work on integer types. Use an if else ladder instead. C++ could obviously have implemented some sort of swich statement for strings - I guess nobody thought it worthwhile, and I agree with them.
Why not? You can use switch implementation with equivalent syntax and same semantics.
The C language does not have objects and strings objects at all, but
strings in C is null terminated strings referenced by pointer.
The C++ language have possibility to make overload functions for
objects comparision or checking objects equalities.
As C as C++ is enough flexible to have such switch for strings for C
language and for objects of any type that support comparaison or check
equality for C++ language. And modern C++11 allow to have this switch
implementation enough effective.
Your code will be like this:
std::string name = "Alice";
std::string gender = "boy";
std::string role;
SWITCH(name)
CASE("Alice") FALL
CASE("Carol") gender = "girl"; FALL
CASE("Bob") FALL
CASE("Dave") role = "participant"; BREAK
CASE("Mallory") FALL
CASE("Trudy") role = "attacker"; BREAK
CASE("Peggy") gender = "girl"; FALL
CASE("Victor") role = "verifier"; BREAK
DEFAULT role = "other";
END
// the role will be: "participant"
// the gender will be: "girl"
It is possible to use more complicated types for example std::pairs or any structs or classes that support equality operations (or comarisions for quick mode).
Features
any type of data which support comparisions or checking equality
possibility to build cascading nested switch statemens.
possibility to break or fall through case statements
possibility to use non constatnt case expressions
possible to enable quick static/dynamic mode with tree searching (for C++11)
Sintax differences with language switch is
uppercase keywords
need parentheses for CASE statement
semicolon ';' at end of statements is not allowed
colon ':' at CASE statement is not allowed
need one of BREAK or FALL keyword at end of CASE statement
For C++97 language used linear search.
For C++11 and more modern possible to use quick mode wuth tree search where return statement in CASE becoming not allowed.
The C language implementation exists where char* type and zero-terminated string comparisions is used.
Read more about this switch implementation.
I think the reason is that in C strings are not primitive types, as tomjen said, think in a string as a char array, so you can not do things like:
switch (char[]) { // ...
switch (int[]) { // ...
In c++ strings are not first class citizens. The string operations are done through standard library. I think, that is the reason. Also, C++ uses branch table optimization to optimize the switch case statements. Have a look at the link.
http://en.wikipedia.org/wiki/Switch_statement
Late to the party, here's a solution I came up with some time ago, which completely abides to the requested syntax.
#include <uberswitch/uberswitch.hpp>
int main()
{
uswitch (std::string("raj"))
{
ucase ("sda"): /* ... */ break; //notice the parenthesis around the value.
}
}
Here's the code: https://github.com/falemagn/uberswitch
You could put the strings in an array and use a constexpr to convert them to indices at compile time.
constexpr const char* arr[] = { "bar", "foo" };
constexpr int index(const char* str) { /*...*/ }
do_something(std::string str)
{
switch(quick_index(str))
{
case index("bar"):
// ...
break;
case index("foo"):
// ...
break;
case -1:
default:
// ...
break;
}
For quick_index, which doesn't have to be constexpr, you could e.g. use an unordered_map to do it O(1) at runtime. (Or sort the array and use binary search, see here for an example.)
Here's a full example for C++11, with a simple custom constexpr string comparer. Duplicate cases and cases not in the array (index gives -1) will be detected at compile time. Missing cases are obviously not detected. Later C++ versions have more flexible constexpr expressions, allowing for simpler code.
#include <iostream>
#include <algorithm>
#include <unordered_map>
constexpr const char* arr[] = { "bar", "foo", "foobar" };
constexpr int cmp(const char* str1, const char* str2)
{
return *str1 == *str2 && (!*str1 || cmp(str1+1, str2+1));
}
constexpr int index(const char* str, int pos=0)
{
return pos == sizeof(arr)/sizeof(arr[0]) ? -1 : cmp(str, arr[pos]) ? pos : index(str,pos+1);
}
int main()
{
// initialize hash table once
std::unordered_map<std::string,int> lookup;
int i = 0;
for(auto s : arr) lookup[s] = i++;
auto quick_index = [&](std::string& s)
{ auto it = lookup.find(s); return it == lookup.end() ? -1 : it->second; };
// usage in code
std::string str = "bar";
switch(quick_index(str))
{
case index("bar"):
std::cout << "bartender" << std::endl;
break;
case index("foo"):
std::cout << "fighter" << std::endl;
break;
case index("foobar"):
std::cout << "fighter bartender" << std::endl;
break;
case -1:
default:
std::cout << "moo" << std::endl;
break;
}
}
hare's comment to Nick's solution is really cool. here the complete code example (in C++11):
constexpr uint32_t hash(const std::string& s) noexcept
{
uint32_t hash = 5381;
for (const auto& c : s)
hash = ((hash << 5) + hash) + (unsigned char)c;
return hash;
}
constexpr inline uint32_t operator"" _(char const* p, size_t) { return hash(p); }
std::string s = "raj";
switch (hash(s)) {
case "sda"_:
// do_something();
break;
default:
break;
}
In C++ you can only use a switch statement on int and char
cout << "\nEnter word to select your choice\n";
cout << "ex to exit program (0)\n";
cout << "m to set month(1)\n";
cout << "y to set year(2)\n";
cout << "rm to return the month(4)\n";
cout << "ry to return year(5)\n";
cout << "pc to print the calendar for a month(6)\n";
cout << "fdc to print the first day of the month(1)\n";
cin >> c;
cout << endl;
a = c.compare("ex") ?c.compare("m") ?c.compare("y") ? c.compare("rm")?c.compare("ry") ? c.compare("pc") ? c.compare("fdc") ? 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0;
switch (a)
{
case 0:
return 1;
case 1: ///m
{
cout << "enter month\n";
cin >> c;
cout << endl;
myCalendar.setMonth(c);
break;
}
case 2:
cout << "Enter year(yyyy)\n";
cin >> y;
cout << endl;
myCalendar.setYear(y);
break;
case 3:
myCalendar.getMonth();
break;
case 4:
myCalendar.getYear();
case 5:
cout << "Enter month and year\n";
cin >> c >> y;
cout << endl;
myCalendar.almanaq(c,y);
break;
case 6:
break;
}
More functional workaround to the switch problem:
class APIHandlerImpl
{
// define map of "cases"
std::map<string, std::function<void(server*, websocketpp::connection_hdl, string)>> in_events;
public:
APIHandlerImpl()
{
// bind handler method in constructor
in_events["/hello"] = std::bind(&APIHandlerImpl::handleHello, this, _1, _2, _3);
in_events["/bye"] = std::bind(&APIHandlerImpl::handleBye, this, _1, _2, _3);
}
void onEvent(string event = "/hello", string data = "{}")
{
// execute event based on incomming event
in_events[event](s, hdl, data);
}
void APIHandlerImpl::handleHello(server* s, websocketpp::connection_hdl hdl, string data)
{
// ...
}
void APIHandlerImpl::handleBye(server* s, websocketpp::connection_hdl hdl, string data)
{
// ...
}
}
You can use switch on strings.
What you need is table of strings, check every string
char** strings[4] = {"Banana", "Watermelon", "Apple", "Orange"};
unsigned get_case_string(char* str, char** _strings, unsigned n)
{
while(n)
{
n--
if(strcmp(str, _strings[n]) == 0) return n;
}
return 0;
}
unsigned index = get_case_string("Banana", strings, 4);
switch(index)
{
case 1: break;/*Found string `Banana`*/
default: /*No string*/
}
You can't use string in switch case.Only int & char are allowed. Instead you can try enum for representing the string and use it in the switch case block like
enum MyString(raj,taj,aaj);
Use it int the swich case statement.
That's because C++ turns switches into jump tables. It performs a trivial operation on the input data and jumps to the proper address without comparing. Since a string is not a number, but an array of numbers, C++ cannot create a jump table from it.
movf INDEX,W ; move the index value into the W (working) register from memory
addwf PCL,F ; add it to the program counter. each PIC instruction is one byte
; so there is no need to perform any multiplication.
; Most architectures will transform the index in some way before
; adding it to the program counter
table ; the branch table begins here with this label
goto index_zero ; each of these goto instructions is an unconditional branch
goto index_one ; of code
goto index_two
goto index_three
index_zero
; code is added here to perform whatever action is required when INDEX = zero
return
index_one
...
(code from wikipedia https://en.wikipedia.org/wiki/Branch_table)
in many cases you can avid extra work by pulling the first char from the string and switching on that. may end up having to do a nested switch on charat(1) if your cases start with the same value. anyone reading your code would appreciate a hint though because most would prob just if-else-if
Switches only work with integral types (int, char, bool, etc.). Why not use a map to pair a string with a number and then use that number with the switch?