Switch statement in C++ with strings?

Started by
18 comments, last by darenking 18 years, 9 months ago
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)
		{
			case "loadsprites":
				file.getline(text, 100);
				CreateSprites(text);
				break;

			case "loadtiles":
				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?
Advertisement
switch does not work with strings, only integral types. see link for details

try this instead.

if (strcmp(text, "loadsprites") == 0) {	[...]} else if (strcmp(text, "loadtiles") == 0) {	[...]} else {	[...]}
Thanks for that reply. That will do the job just fine, but does anyone know of any other solutions?
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
Quote:Original post by darenking
Thanks 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.
HardDrop - hard link shell extension."Tread softly because you tread on my dreams" - Yeats
is there a reason why you need another solution?
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
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.
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.

This topic is closed to new replies.

Advertisement