I am new in c++ programming and i am trying to understand exceptions in c++. I made a simple model situation that shows things, which I don't understand(I hope, I wont mess up code too much). I made 2 basic classes with few methods(classes CPerson are basically linked list). My answer is how to stop current task with an exception. I am able to call an exception, but task continues and makes some mess in program.
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
class CPerson{
public:
CPerson(){
p_next_person = NULL;
}
CPerson* p_next_person; // pointer to next person in linked list
int Postcode(); // returns postcode of person
friend ostream& operator<<(ostream& stream, const CPerson& pers){
cout << pers.ID << pers.postcode;
return stream;
}
char* ID;
int postcode;
};
//---------------------------------------------------------------
class CPeople{
public:
CPeople(){
first_person = NULL;
}
CPerson Person( const char* personID); // finds person by ID and returns it
bool NewPerson( const char* personID, int person_postcode); // add new person
CPerson* first_person ; // start of linked list
};
//-----------------------------------------------------------------
int CPerson::Postcode(){
return postcode;
}
//-----------------------------------------------------------------
CPerson CPeople::Person( const char* personID){
CPerson* now;
now = first_person;
while(now != NULL){
if(strcmp(now->ID,personID)==0){
break;
}
now = now->p_next_person;
}
// our person is in now (or now is NULL - if person wasn't found).
try{
if(now == NULL ){
throw 0;
// I need to stop code here
}else return *now;
}
catch (int e)
{
cout << "bla bla " << e << '\n';
}
}
//----------------------------------------------------------
int main(){
CPeople people;
int i = 0;
people.NewPerson( "JAck", 100 );
people.NewPerson( "Josh", 100 );
// Bob is not in people right now.
i = people.Person("BOB").Postcode();
cout << i;
// gives exception, which is nice. but it also changes i to some nonsence .. how do I fix it ?
cout << people.Person ( "BOB" );
// gives exception, which is nice. but also gives segmentation fault. how do I fix it ?
}
You have got the try block around 'throw. The try block should be around where you called the function and it should be caught with a catch. Thus your function will change to:
CPerson CPeople::Person( const char* personID){
CPerson* now;
now = first_person;
while(now != NULL){
if(strcmp(now->ID,personID)==0){
break;
}
now = now->p_next_person;
}
// our person is in now (or now is NULL - if person wasn't found).
if (now == NULL ){
throw 0;
// I need to stop code here
}
else return *now;
}
and main will look like:
int main(){
try {
CPeople people;
int i = 0;
people.NewPerson( "JAck", 100 );
people.NewPerson( "Josh", 100 );
// Bob is not in people right now.
i = people.Person("BOB").Postcode();
cout << i;
// gives exception, which is nice. but it also changes i to some nonsence .. how do I fix it ?
cout << people.Person ( "BOB" );
// gives exception, which is nice. but also gives segmentation fault. how do I fix it ?
}
catch (int e)
{
cout << "bla bla " << e << '\n';
}
}
Notice that once a catch is encountered, the following statement after the catch will be executed. That is why you should have catch out of the function definition.
Code like this
try{
if( now == NULL ){
throw 0;
// I need to stop code here
} else return *now;
} catch (int e) {
cout << "bla bla " << e << '\n';
}
entirely misses the point. Continuing execution with a shrug ("bla bla") as if nothing had happened isn't possible. Either you make sure that all contingencies are met in the catch, or you should catch the exception at a higher level. Here: there is no definition of the function's return value, which causes trouble up there where you call CPeople::Person.
You can surround these calls with a try - catch; omit them in the function and just throw.
Don't throw 0. Use an object capable of holding some information. Throw by value, catch by reference.
Background: I'm trying to figure out how to implement continuations/coroutines/generators (whatever the following is called) by posing this toy problem. The environment is C++11 on gcc 4.6 and linux 3.0 x86_64. Non-portable is fine but using an external library (boost.coroutine, COROUTINE, etc) is not allowed. I think longjmp(3) and/or makecontext(2) and friends may help but not sure.
Description:
The following toy parser is supposed to parse sequences of as and bs of equal length. ie
((a+)(b+))+
such that the length of the second bracketed production equals the third.
When it finds a production (eg aaabbb) it outputs the number of as it finds (eg 3).
Code:
#include <stdlib.h>
#include <iostream>
using namespace std;
const char* s;
void yield()
{
// TODO: no data, return from produce
abort();
}
void advance()
{
s++;
if (*s == 0)
yield();
}
void consume()
{
while (true)
{
int i = 0;
while (*s == 'a')
{
i++;
advance();
}
cout << i << " ";
while (i-- > 0)
{
if (*s != 'b')
abort();
advance();
}
}
}
void produce(const char* s_)
{
s = s_;
// TODO: data available, continue into consume()
consume();
}
int main()
{
produce("aaab");
produce("bba");
produce("baa");
produce("aabbb");
produce("b");
// should print: 3 1 4
return 0;
}
Problem:
As you can see the state of the consume call stack must be saved when yield is called and then produce returns. When produce is called again, consume must be restarted by returning from yield. The challenge would be to modify the way produce calls consume, and implement yield so they function as intended.
(Obviously reimplementing consume so that it saves and rebuilds its state defeats the purpose of the exercise.)
I think what needs to be done is something like the example on the bottom of the makecontext man page: http://www.kernel.org/doc/man-pages/online/pages/man3/makecontext.3.html, but its not clear how to translate it onto this problem. (and I need sleep)
Solution:
(Thanks to Chris Dodd for design)
#include <stdlib.h>
#include <iostream>
#include <ucontext.h>
using namespace std;
const char* s;
ucontext_t main_context, consume_context;
void yield()
{
swapcontext(&consume_context, &main_context);
}
void advance()
{
s++;
if (*s == 0)
yield();
}
void consume()
{
while (true)
{
int i = 0;
while (*s == 'a')
{
i++;
advance();
}
cout << i << " ";
while (i-- > 0)
{
advance();
}
}
}
void produce(const char* s_)
{
s = s_;
swapcontext(&main_context, &consume_context);
}
int main()
{
char consume_stack[4096];
getcontext(&consume_context);
consume_context.uc_stack.ss_sp = consume_stack;
consume_context.uc_stack.ss_size = sizeof(consume_stack);
makecontext(&consume_context, consume, 0);
produce("aaab");
produce("bba");
produce("baa");
produce("aabbb");
produce("b");
// should print: 3 1 4
return 0;
}
Its fairly straight-forward to use makecontext/swapcontext for this -- you use makecontext to create a new coroutine context and swapcontext to swap between them. In you case, you need one additional coroutine to run the consume infinite loop, and you run main and produce in the main context.
So main should call getcontext+makecontext to create a new context that will run the consume loop:
getcontext(&consume_ctxt);
// set up stack in consume_context
makecontext(&consume_ctxt, consume, 0);
and then produce will switch to it instead of calling consume directly:
void produce(const char* s_)
{
s = s_;
swapcontext(&main_ctxt, &consume_ctxt);
}
and finally yield just calls swapcontext(&consume_ctxt, &main_ctxt); to switch back to the main context (which will continue in produce and immediately return).
Note that since consume is an infinite loop, you don't need to worry too much about what happens when it returns (so the link will never be used)
I'm getting some weird behavior with a vector in C++ I was hoping someone could help me out. I have a vector like so:
vector<Instruction*> allInstrs;
the struct for Instruction is as follows:
struct Instruction : simple_instr
{
InstrType type;
Instruction(const simple_instr& simple) : simple_instr(simple)
{
type = Simple;
loopHeader = false;
loopTail = false;
}
int Id;
bool loopHeader;
bool loopTail;
};
the problem I'm having is this:
I need to iterate through each instruction and pull out specific fields and use those to do some analysis on the instructions in the vector. To do that, I was basically doing
VariableList Variables;
void GenerateVariableList()
{
for (int i = 0; i < allInstrs.size(); i++)
{
Variables.Add(allInstrs[i]);
}
Variables.RemoveDuplicates();
}
Variable List is defined as
struct VariableList
{
void Add(simple_instr* instr)
{
PrintOpcode(instr);
switch(instr->opcode)
{
case STR_OP:
case MCPY_OP:
Add(instr->u.base.src1);
Add(instr->u.base.src2);
break;
case LDC_OP:
Add(instr->u.ldc.dst);
break;
case BTRUE_OP:
case BFALSE_OP:
Add(instr->u.bj.src);
break;
case CALL_OP:
cout << "CALL OP" <<endl;
break;
case MBR_OP:
Add(instr->u.mbr.src);
break;
case RET_OP:
if (instr->u.base.src1 != NO_REGISTER)
Add(instr->u.base.src1);
break;
case CVT_OP:
case CPY_OP:
case NEG_OP:
case NOT_OP:
case LOAD_OP:
Add(instr->u.base.dst);
Add(instr->u.base.src1);
break;
case LABEL_OP:
case JMP_OP:
break;
default:
Add(instr->u.base.dst);
Add(instr->u.base.src1);
Add(instr->u.base.src2);
break;
}
}
void Add(Variable var)
{
variableList.push_back(var);
}
void RemoveDuplicates()
{
if (variableList.size() > 0)
{
variableList.erase(unique(variableList.begin(), variableList.end()), variableList.end());
currentID = variableList.size();
}
}
VariableList()
{
currentID = 0;
}
VariableList(VariableList& varList, bool setLiveness = false, bool LiveVal = false)
{
currentID = 0;
for (int i = 0; i < varList.size(); i++)
{
Variable var(varList[i]);
if (setLiveness)
{
var.isLive = LiveVal;
}
variableList.push_back(var);
}
}
Variable& operator[] (int i)
{
return variableList[i];
}
int size()
{
return variableList.size();
}
vector<Variable>::iterator begin()
{
return variableList.begin();
}
vector<Variable>::iterator end()
{
return variableList.end();
}
protected:
int currentID;
vector<Variable> variableList;
void Add(simple_reg* reg, bool checkForDuplicates = false)
{ cout << "Register Check" <<endl;
if (reg == null)
{
cout << "null detected" << endl;
return;
}
if (reg->kind == PSEUDO_REG)
{
if (!checkForDuplicates || (checkForDuplicates && find(variableList.begin(), variableList.end(), reg->num) != variableList.end()))
{
cout << "Adding... Reg " << reg->num << endl;
Variable var(reg->num, currentID);
variableList.push_back(var);
currentID++;
}
}
}
};
When I do this though, every instruction goes to the default case statement, even though I knwo for a fact some instructions shouldn't. If I change GenerateVariableList to
void GenerateVariableList()
{
for (int i = 0; i < allInstrs.size(); i++)
{
PrintOpcode(allInstrs[i]);
Variables.Add(allInstrs[i]);
}
Variables.RemoveDuplicates();
}
so that there is now a second PrintOpCode in addition to the one in Variables.Add, the program behaves correctly. I can't understand why adding a second PrintOpcode makes it work correctly. All print Opcode is is a function with a switch statement that just prints out a specific string depending on what the value of one of simple_instr's fields is.
VariableList Variables is contained inside of a separate struct called CFG
If you need more information/code i can provide it. If the answer is obvious I apologize, I don't program in C++ very often
EDIT:
One of the answers left, deleted now though, got me the fix.
Previously I was doing
static vector<Instruction*> ConvertLinkedListToVector(simple_instr* instructionList)
{
vector<Instruction*> convertedInstructions;
int count = 0;
for (simple_instr* current = instructionList; current; count++, current = current->next)
{
//Instruction* inst = new Instruction(*current);
Instruction inst = Instruction(*current);
inst.Id = count;
convertedInstructions.push_back(&inst);
}
return convertedInstructions;
}
to make the vector, but after reading that answer I changed it back to using "new" and it works correctly now. Thanks for the help, sorry for the dumb question heh
Most likely the const simple_instr& simple passed to your constructor goes out of scope, and you keep an invalid reference/pointer to a simple_instr.
Possibly not related your problem, but certainly a potential source of strange behaviour: Your Instruction(const simple_instr& simple) constructor may be getting called when you don't intend it. Mark it explicit...
explicit Instruction(const simple_instr& simple) ...
If that causes compiler errors, then that's progress :-) You might need to write a copy constructor to make them go away, and explicitly call the old constructor where you need to.
So, there are several suspicious observations:
In your definition of VariableList you use a type called Variable - how is that type defined?
Iterating over a container should be done using an iterator:
for (vector<Intruction *>::iterator it = allInstrs.begin();
it != allInstrs.end();
++it) {
Variables.Add(*it);
}
You should consider using a vector of boost::shared_ptr, or a boost::ptr_vector instead of a vector of pointers.
I can give you a huge general overview of "don'ts" relating to your code.
You are right in this case to use classes "deriving" from simple_instr but you are doing it wrong, given that later on you do a switch statement based on type. A switch-statement based on type (rather than state) is an anti-pattern. You should be calling some virtual method of your base class.
You almost certainly do not want your derived class to copy from the base class. You want to construct it with the parameters to construct its base-class.
You want a vector of the base class pointers? And to manage lifetime probably shared_ptr
const-correctness. Some of your methods like size() should certainly be const. For others you might want two overloads.
EDIT: After even more code modification, the error is still there, modified code shown:
KeyDown():
const int input_bit_num = 0x8000;
char keys[256];
bool KeyDown(int key)
{
return (keys[key] & input_bit_num) != 0;
}
PollKeyboard():
LPDIRECTINPUTDEVICE8 di_keyboard;
void PollKeyboard()
{
long result = di_keyboard->GetDeviceState(sizeof(keys), (LPVOID)&keys);
char para[16];
itoa(result, para, 17);
if(result != DI_OK) MessageBox(NULL, para, "ERROR", MB_OK);
}
When I try to put MessageBox within a KeyDown() if statement (as seen below in the game loop), the MessageBox simply coninues to appear even if I stop pressing the key, ie: I press , the "Would you like to quit?" message box appears, I say no, It disappears and then instantly reappears, as if I were still holding the key.
This is my loop:
void GameRun(HWND hWnd) //called once every frame
{
PollKeyboard();
if(GetTickCount - start >= 30)
{
if(KeyDown(DIK_LEFT))
MoveLeft();
if(KeyDown(DIK_RIGHT))
MoveRight();
}
if(d3ddev->BeginScene())
{
//rendering
}
if(KeyDown(DIK_ESCAPE))
{
//any MessageBox()
int result = MessageBox(hWnd, "I'm causing so much trouble!", "IMMORTAL", MB_YESNOCANCEL);
if(result == IDYES)
//end
}
}
EDIT: The catch in PollKeyboard() displays the sequence 53gd6bcc, I could not, however, find the error code it corresponds to.
EDIT: After another test, I saw that even if the MessageBox is not within a KeyDown() if statement, the glitch still occurs.
EDIT: After a bit more testing, it appears that MessageBox itself is causing the glitch.
Because the sample code works, something else in your program is causing the bug. Try moving bits of the code below into your own until it works, then you will know which section of code was culprit.
Sample Code
Alright, huge code block coming up. This code works correctly for me. (Escape, along with all other keys successfully activate and deactivate). It's large, commented, and explains things fairly well. Try this, if it works, we'll examine other parts of your program, if not, I can only leave you with "Good luck", and take what you want:
// DirectInput
#define DIRECTINPUT_VERSION 0x0800
#include<dinput.h>
// Standard stuff
#include <iostream>
#include <stdexcept>
#include <sstream>
#include <string>
// Link from code, MSVC specific, could be done in project settings
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")
// Utility lexical_cast, use Boost if possible.
// Simple replacement, converts a stream-able `T`
// to a string
template <typename T>
const std::string lexical_cast(const T& pValue)
{
std::stringstream ss;
ss << pValue;
return ss.str();
}
// Utility function + macro to execute DirectX code with exceptions.
// Kinda ugly, but helpful for us.
void check_error(HRESULT pResult, const std::string& pFuncName)
{
// DI_OK == S_OK, but S_OK is more general, so we'll use that
if (pResult != S_OK)
{
throw std::runtime_error("Error executing: " + pFuncName +
"! Returned: " + lexical_cast(pResult));
}
}
// Macro, makes calling the function easier. It is wrapped in
// an `if` statement for reasons outlined in:
// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.5
#define CHECK_ERROR(x) if (true) { check_error(x, #x); } else (void)0
// The above gives the warning:
// "warning C4127: conditional expression is constant", disable below:
#pragma warning(disable: 4127)
// Manages input
class input_manager
{
public:
// Constants
static const int NumberKeys = 256;
// Creation
input_manager(void)
{
// Create input and keyboard (like I said, ugly macro, but helpful :] )
CHECK_ERROR(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
IID_IDirectInput8, reinterpret_cast<void**>(&_input), 0));
CHECK_ERROR(_input->CreateDevice(GUID_SysKeyboard, &_keyboard, 0));
CHECK_ERROR(_keyboard->SetDataFormat(&c_dfDIKeyboard));
CHECK_ERROR(_keyboard->Acquire());
}
~input_manager(void)
{
// Free resources. Note: Many programmers
// unnecessarily wrap this stuff in
// `if (_keyboard !=0)`, and then
// `_keyboard = 0`. This is completely unnecessary,
// because destructors are only run one time.
// Also, I can assume they are valid, because if they
// weren't, we wouldn't be here (an exception would have
// been thrown)
_keyboard->Unacquire();
_keyboard->Release();
_input->Release();
// Also, if we wrapped this into a nice RAII class, we wouldn't
// be forced to write a destructor, but this is outside the scope.
// Feel free to ask how; additionally, since we're on the topic, if you'd
// like more tips handling input (I've written PLENTY of input managers)
// I'm free for asking about things like testing for triggers rather than pressed
// ("was it pressed, regardless if it's being held now" versus
// "is it being pressed"), etc.
}
// Operations
void update(void)
{
CHECK_ERROR(_keyboard->GetDeviceState(NumberKeys, reinterpret_cast<void*>(&_keys)));
}
// Query
bool key_pressed(int pKey) const
{
return test_key(pKey);
}
// Might wrap into an operator[] for convenience.
private:
// Constants
static const int PressMask = 0x80;
// Sorry for the confusion, but indeed, with
// `char`s the mask is simply 0x80.
// Utility
bool test_key(int pKey) const
{
return (_keys[pKey] & PressMask) != 0;
}
// Members
LPDIRECTINPUT8 _input;
LPDIRECTINPUTDEVICE8 _keyboard;
char _keys[NumberKeys];
};
void test_keys(const input_manager& input)
{
bool anyPressed = false;
for (unsigned i = 0; i < input_manager::NumberKeys; ++i)
{
if (input.key_pressed(i))
{
std::cout << "Pressing: " << i << std::endl;
anyPressed = true;
}
}
if (!anyPressed)
{
std::cout << "No keys pressed." << std::endl;
}
}
void execute(void)
{
input_manager input;
std::cout << "Press Q to quit." << std::endl;
bool running = true;
while (running)
{
input.update();
if (input.key_pressed(DIK_Q))
{
running = false;
}
test_keys(input);
Sleep(0); // give some processor time
}
}
int main(void)
{
// Place real code in an execute function, so main
// is clean and ready to catch exceptions:
try
{
execute();
}
catch (const std::exception& e)
{
// Error!
std::cerr << "Unhandled exception:" << e.what() << std::endl;
}
}
Old suggestion:
Try catching the return value from GetDeviceState:
HRESULT result = // v Prefer C++-style casts
di_keyboard->GetDeviceState(sizeof(keys), reinterpret_cast<void*>(&keys);
if (result != DI_OK)
{
// uh-oh
std::cout << result << std::endl;
}
Compare it against the table here.
Old Semi-Answer:
Shortly after editing in the code in the Extra Stuff section, I realized the error, sorry I didn't catch it earlier. You're testing the wrong bit :)
Observe:
// v HERE! Should be 0x8000, not 0x80.
return (GetAsyncKeyState(pKeyCode) & 0x8000) != 0;
Try that:
int KeyDown(int key)
{
return (keys[key] & 0x8000);
}
Also, this should be moved into a constant to avoid magic numbers:
// somewhere, probably in the private section of the class or in a detail namespace:
static const int PushedMask = 0x8000;
// code reads better:
int KeyDown(int key)
{
return (keys[key] & PushedMask);
}
Lastly, in C++ you have a bool type, so take advantage of it!
// v here
bool KeyDown(int key)
{
return (keys[key] & PushedMask);
}
I know Visual Studio will warn about this conversion from int to bool, so you can get rid of it while also making your intents clearer:
bool KeyDown(int key)
{
return (keys[key] & PushedMask) != 0; // or == 1, your choice
}
Extra Stuff:
Try the following code:
#include <iostream>
#include <windows.h>
bool key_pressed(int pKeyCode)
{
return (GetAsyncKeyState(pKeyCode) & 0x8000) != 0;
}
void test_keys(void)
{
for (unsigned i = 0; i < 255; ++i)
{
if (key_pressed(i))
{
std::cout << "Pressing: " << i << std::endl;
}
}
}
int main(void)
{
bool running = true;
while (running)
{
if (key_pressed(VK_ESCAPE))
{
running = false;
}
test_keys();
Sleep(0);
}
}
This works for me (responds to all keys, quits on escape). Minimal test case for GetAsyncKeyState. If this does not work, please add OS, Keyboard, etc, in your comment.
If you create a MessageBox(Null,...) you won't have any control over the window after it's creation. IE, the window won't disappear when you depress the key.
As for why it keeps on appearing, seems to have something to do with this:
const int input_bit_num = 0x8000;
char keys[256];
bool KeyDown(int key)
{
return (keys[key] & input_bit_num) != 0;
}
keys consits of 1 byte long characters, while input_bit_num is a 2 byte value. While I don't honestly know which bit it is that you're looking for (0xff - 0x00 is the domain of 1 byte).
Honestly, I'm surprised that your code runs, unless the & operation is carrying over into keys[key-1] in which case any KeyDown is undefined, and KeyDown(...) when key is 0 is particularly dangerous.
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?