C/C++ switch case with string [duplicate]
Possible Duplicate:
C/C++: switch for non-integers
Hi, I need to use a string in s开发者_开发百科witch case. My solution so far was to calculate the hash of the string with my hash function. Problem is I have to manually pre-calculate all my hash values for strings. Is there a better approach?
h=_myhash (mystring);
switch (h)
{
case 66452:
.......
case 1342537:
........
}
Just use a if() { } else if () { }
chain. Using a hash value is going to be a maintenance nightmare. switch
is intended to be a low-level statement which would not be appropriate for string comparisons.
You could map the strings to function pointer using a standard collection; executing the function when a match is found.
EDIT: Using the example in the article I gave the link to in my comment, you can declare a function pointer type:
typedef void (*funcPointer)(int);
and create multiple functions to match the signature:
void String1Action(int arg);
void String2Action(int arg);
The map would be std::string
to funcPointer
:
std::map<std::string, funcPointer> stringFunctionMap;
Then add the strings and function pointers:
stringFunctionMap.add("string1", &String1Action);
I've not tested any of the code I have just posted, it's off the top of my head :)
Typically, you would use a hash table and function object, both available in Boost, TR1 and C++0x.
void func1() {
}
std::unordered_map<std::string, std::function<void()>> hash_map;
hash_map["Value1"] = &func1;
// .... etc
hash_map[mystring]();
This is a little more overhead at runtime but a bajillion times more maintainable. Hash tables offer O(1) insertion, lookup, and etc, which makes them the same complexity as the assembly-style jump-table.
The best way is to use source generation, so that you could use
if (hash(str) == HASH("some string") ..
in your main source, and an pre-build step would convert the HASH(const char*)
expression to an integer value.
You could create a hashtable. The keys can be the string and the value can be and integer. Setup your integers for the values as constants and then you can check for them with the switch
.
Ruslik's suggestion to use source generation seems like a good thing to me. However, I wouldn't go with the concept of "main" and "generated" source files. I'd rather have one file with code almost identical to yours:
h=_myhash (mystring);
switch (h)
{
case 66452: // = hash("Vasia")
.......
case 1342537: // = hash("Petya")
........
}
The next thing I'd do, I'd write a simple script. Perl is good for such kind of things, but nothing stops you even from writing a simple program in C/C++ if you don't want to use any other languages. This script, or program, would take the source file, read it line-by-line, find all those case NUMBERS: // = hash("SOMESTRING")
lines (use regular expressions here), replace NUMBERS with the actual hash value and write the modified source into a temporary file. Finally, it would back up the source file and replace it with the temporary file. If you don't want your source file to have a new time stamp each time, the program could check if something was actually changed and if not, skip the file replacement.
The last thing to do is to integrate this script into the build system used, so you won't accidentally forget to launch it before building the project.
You could use the string to index into a hash table of function pointers.
Edit: glib has a hash table implementation that supports strings as keys and arbitrary pointers as values: http://library.gnome.org/devel/glib/stable/glib-Hash-Tables.html
You can use enumeration and a map, so your string will become the key and enum value is value for that key.
If you are after performance and don't want to go through all the if
clauses each time if there are many or the need to hash the values, you could send some extra information to the function with the help of enum
or just add an enum
type to your structure.
There is no good solution to your problem, so here is an okey solution ;-)
It keeps your efficiency when assertions are disabled and when assertions are enabled it will raise an assertion error when the hash value is wrong.
I suspect that the D programming language could compute the hash value during compile time, thus removing the need to explicitly write down the hash value.
template <std::size_t h>
struct prehash
{
const your_string_type str;
static const std::size_t hash_value = h;
pre_hash(const your_string_type& s) : str(s)
{
assert(_myhash(s) == hash_value);
}
};
/* ... */
std::size_t h = _myhash(mystring);
static prehash<66452> first_label = "label1";
switch (h) {
case first_label.hash_value:
// ...
;
}
By the way, consider removing the initial underscore from the declaration of _ myhash() (sorry but stackoverflow forces me to insert a space between _ and myhash). A C++ implementation is free to implement macros with names starting with underscore and an uppercase letter (Item 36 of "Exceptional C++ Style" by Herb Sutter), so if you get into the habit of giving things names that start underscore, then a beautiful day could come when you give a symbol a name that starts with underscore and an uppercase letter, where the implementation has defined a macro with the same name.
精彩评论