# Switch statement in C++ with strings?

This topic is 4611 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Howdy! I'm writing a routine that triggers the loading in of sprites (or tile images or whatever) by reading instructions from a text file. The text file contains instructions like this: loadsprites data/001_sprites_paula.bmp loadsprites data/001_sprites_smally.bmp I wanted to do a switch statement like this:
		file.getline(text, 100);

switch (text)
{
file.getline(text, 100);
CreateSprites(text);
break;

file.getline(text, 100);
CreateTiles(text);
break;

default:
}


...so that when it finds the instruction 'loadsprites' it will load in the bitmap named in the next line. But it seems you can't do a switch statement with a string of characters! Any ideas?

##### Share on other sites
switch does not work with strings, only integral types. see link for details

if (strcmp(text, "loadsprites") == 0) {	[...]} else if (strcmp(text, "loadtiles") == 0) {	[...]} else {	[...]}

##### Share on other sites
Thanks for that reply. That will do the job just fine, but does anyone know of any other solutions?

##### Share on other sites
Depends how extendable you want your system to be. I did something kind of similar in my last project, as I had a quake like console, which could execute commands typed in by the user. The way I implemented it, was to have the console store an array of Command objects. Each command had a method which accepted an array of arguments, and a method that returned a unique integer hash value generated from the commands name.

When the command line is parsed, the console generates a unique hash code from the command name typed in at the command line, and searches its list of commands for a
command that has a matching hash code (and therefore a matching name).

To generate the hash code, I used the CRC32 algorithm. There's an implementation of it in the boost library.

If you don't want to go that far, then something like this would probably suffice.

//Do this once at initialisation timeUInt loadSprites = GenerateHash ( "LoadSprites" );UInt loadTiles   = GenerateHash ( "LoadTiles" );//At load timecommandName = GetCommandNameFromFile();UInt commandHash = GenerateHash ( commandName );switch ( commandHash ){       case loadSprites:             //Blah              case loadTiles:            //What}

If you're interested in seeing how I implemented my solution, then the source code to my old project is available here. I think Console.h and Console.cpp are the relevant files. ConsoleCommand.h and ConsoleVariable.h might also be of interest.

NO! :P

##### Share on other sites
Quote:
 Original post by darenkingThanks for that reply. That will do the job just fine, but does anyone know of any other solutions?

you could use a std::map< string, enum/int> and simply do a lookup.

##### Share on other sites
is there a reason why you need another solution?

##### Share on other sites
A common way of solving this, is to first convert your string to a numeral like this:
#define INSTRUCTION_TYPE_LOADSPRITES     1#define INSTRUCTION_TYPE_LOADTILES       2#define INSTRUCTION_TYPE_UNKNOWN         3int GetInstructionType(char *text){    if (strcmp(text, "loadsprites") == 0) {	return INSTRUCTION_TYPE_LOADSPRITES;    } else if (strcmp(text, "loadtiles") == 0) {	return INSTRUCTION_TYPE_LOADTILES;    } else 	return INSTRUCTION_TYPE_UNKNOWN;}

and then change your switch statement as follows:

file.getline(text, 100);switch (GetInstructionType(text)){    case INSTRUCTION_TYPE_LOADSPRITES:	file.getline(text, 100);	CreateSprites(text);	break;   case INSTRUCTION_TYPE_LOADTILES:	file.getline(text, 100);	CreateTiles(text);	break;   default:        break;}

It could be made even nicer by using an enum, but this should be clear enough.

Note that if you have to compare the same instruction string many times, this conversion to an integer is also faster, because you'll need only one string comparison to get the int.

Tom

##### Share on other sites
You could try something like this:
typedef void (*CreateFunc)(std::string text);std::map<std::string, CreateFunc> lookuptable;lookuptable["loadsprites"] = CreateSprites;lookuptable["loadtiles"] = CreateTiles;std::string input = std::getline(file);if(lookuptable.count(input) <= 0) {  // Error! Invalid command}std::string text = std::getline(file);lookuptable[input](text);

I haven't used C++ in a while, so this might not be completely correct.

EDIT:
Wow, I should really stop taking so much time to post.
Quote:
 Depends how extendable you want your system to be. I did something kind of similar in my last project, as I had a quake like console, which could execute commands typed in by the user. The way I implemented it, was to have the console store an array of Command objects. Each command had a method which accepted an array of arguments, and a method that returned a unique integer hash value generated from the commands name.

For the love of god, no! A std::map should do just fine without complicating everything like that. Even if it turns out you need to hash the strings, you should probably use something like std::hash_map.

##### Share on other sites
EDIT: I am mindnumbingly slow.

Or you could do another way, for instance:

// A function pointer that represent script functionstypedef void (*scriptFunction)(std::istream&);// A context: it associates a name (the function name or the variable name) // with an object (a script function)typedef std::map<std::string,scriptFunction> context; void CreateSprites(std::istream&);void CreateTiles(std::istream&);// Parsing a script document:void parse( std::istream& in ) {  context c;  c["loadsprites"] = CreateSprites;  c["loadtiles"] = CreateTiles;  string command;  while( !std::getline( in, command ) ) {        c[command](in);      }}Note: the above is unsafe, you should always check that c[command] is an actual function, or your script can make your program crash.Additional advantages: if you replace the function pointer by a (non-pointer) object, you can do just about anything.

##### Share on other sites
Quote:
 Original post by kusmais there a reason why you need another solution?

Only that it seems less tidy than a switch... so another solution would only be worthwhile (in my case, as I only have half dozen or so instructions) if it's really simple.

##### Share on other sites
a switch is usally expanded to a series of compares and/or a jump-table. as you can't just compare the pointers, nor use them for a jump-table, you're stuck with string-compares unless you want to go for something fancy like hashing. anyway, this looks to me like init-code, and if it is... forget optimizing this kind of stuff, it's simply not worth it.

##### Share on other sites
The map approach is clean, straightforward, and reasonably optimal (better than the if/else chain of the "most obvious working" code); use it.

If it's *really* speed critical (and I *really highly* doubt it), you might consider a hashed map implementation like stdext::hash_map (I think that's it) rather than std::map (which is a tree map).

##### Share on other sites
First, I'd like to point out that there's a much better alternative to:

char text[100];
file.getline( text , 100 );

And that is:

std::string text;
std::getline( file , text );

This will read past 100 characters, expanding text as needed.

I will use this along with std::map to show one of the ways you can achieve the same effect as a switch, if somewhat more split up, which should also handle invalid input files (e.g. with "loadmymomma" instead of a valid command like "loadsprites") without crashing:

void do_loadsprites( std::istream & is ) {    std::string text;    std::getline( is , text );    CreateSprites( text );}void do_loadtiles( std::istream & is ) {    std::string text;    std::getline( is , text );    CreateTiles( text );}void function_that_had_the_switch_statement( void ) {    std::ifstream file( "MyData.txt" ); //example    std::map< std::string , void (*)( std::istream & ) > do_load_actions;    do_load_actions[ "loadsprites" ] = & do_loadsprites;    do_load_actions[ "loadtiles" ] = & do_loadtiles;    ...    //in place of your switch statement:    std::string text;    std::getline( file , text );    if ( do_load_actions.find( text ) == do_load_actions.end() ) {        /* error, text wasn't "loadsprites" or "loadtiles". As such, there's no         * entry in our map for it. You'd place your code for the "default:"         * label here.         */    } else {        do_load_actions[ text ](); //runs the function pointed at    }}

##### Share on other sites
Quote:
 Original post by ZahlmanThe map approach is clean, straightforward, and reasonably optimal (better than the if/else chain of the "most obvious working" code); use it.If it's *really* speed critical (and I *really highly* doubt it), you might consider a hashed map implementation like stdext::hash_map (I think that's it) rather than std::map (which is a tree map).
My thoughts exactly.
I would also suggest using a map from a string to an enum, and perhaps do a switch on the enum.
A recent experience has shown that this is a place where hash_map does indeed make an enourmous difference if you have plenty of strings (as we did).

##### Share on other sites
Quote:
 Original post by ZahlmanThe map approach is clean, straightforward, and reasonably optimal (better than the if/else chain of the "most obvious working" code); use it.

if youy have a lot of strings to compare against, yes. however, in this case he had two. now think about it, how much use is there in a tree with two nodes? ;)

[edit:] after reading my reply, i realise i sound like i dislike the map-approach, sorry about that. it IS generaly the superiour way, but i kinda dislike premature optimizations ;)

[Edited by - kusma on July 5, 2005 5:09:01 AM]

##### Share on other sites
Yeah, that's why I put "clean" and "straightforward" first and "reasonably optimal" after that (and then only "reasonably", because yes you will just pay overhead for the tree structure with small N). :)

##### Share on other sites
Have you considered the Lua scripting language? I know its off topic, but it may be an alternate solution for you, rather than writing this type of parser, you could apply Lua to solve any of your other circumstances that may come up.

##### Share on other sites
Quote:
Original post by kusma
Quote:
 Original post by ZahlmanThe map approach is clean, straightforward, and reasonably optimal (better than the if/else chain of the "most obvious working" code); use it.

if youy have a lot of strings to compare against, yes. however, in this case he had two. now think about it, how much use is there in a tree with two nodes? ;)

[edit:] after reading my reply, i realise i sound like i dislike the map-approach, sorry about that. it IS generaly the superiour way, but i kinda dislike premature optimizations ;)
Actually I assumed (and still do) that he simply posted only two cases in his code because it gives you the general idea without posting way more code than necessary.

If in fact he does have only two then just use a couple of it statements and be done with it.

##### Share on other sites
I have about five at the mo. May end up with twenty or so at some point.