Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 585
  • Last Modified:

Switch Case with Cstring variable

How can I use a switch case with a CString variable?

Thanks
0
oliverUK
Asked:
oliverUK
  • 5
  • 5
  • 3
  • +4
1 Solution
 
SalteCommented:
Isn't there a class member function that converts case to upper/lower case as needed? I remember there should be some function like that.

Check your documentation.

If you can't find it you can use this method:

CString to_upper(const CString & str)
{
   char * p = new char[str.Length() + 1];
   strupr(p,str);  // (*)
   CString x = p;
   delete [] p;
   return x;
}

Not 100% sure if strupr act like a strcpy with conversion to uppercase as I assume above or if it just take one argument and modifies the string. If it is a one argument function then you do it like this:

CString to_upper(const CString & str)
{
   char * p = new char[str.Length() + 1];
   strcpy(p,str);
   strupr(p);  // (*)
   CString x = p;
   delete [] p;
   return x;
}

Hope this is of help. If you want to change to lowercase the similar function is named strlwr(), again either one or two arguments, can't remember which at the moment.

Alf
0
 
oliverUKAuthor Commented:
Sorry my question was ambiguous,

I actually meant how can a use a CString  variable in a "switch" statement.
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
AxterCommented:
You would have to use some type of function that would convert your CString to a number.
switch statements only work with numbers.
So if your CString is a string number, then you can do the following:

switch(atoi(MyCString))

Other wise, you need to use an indexing function in order to use it with switch.
0
 
codez80Commented:
oliverUK,

There is no real and effective way of doing that.
You can use what AlexFM suggested to make your code more readable but it does not have the advantages of a real switch statement, such as is jumping straight to the point of the constant.
The main reason is that switch statements rely on const integers internally when the code is compiled.
I assume that you already know the alternative:

if (strcmp(str, "hey") == 0)
{
  // do this
}
else if (strcmp(str, "aha") == 0)
{
  // do that
}
else if (strcmp(str, "xxx") == 0)
{
}

etc.


codez80


0
 
AxterCommented:
Salte,
FYI, CString does have it's own MakeUpper and MakeLower functions.
0
 
fsign21Commented:
You can not use CString in switch directly.
Recall the description of the switch-statement:

switch(expression){
   case constant_1:
      // code to execute if integer_val is val_1
      break;
    ...
   case constant_n:
      // code to execute if integer_val is val_n
      break;
   default:
      // code to execute if integer_val is none of the above
}
Note, that "expression" must evaluate to an integer type and that the "constant_s" must be integer constants (which include chars).

You have to use if() - else if() - else statements.

-----

Alternatively, you could program some kind of mapping of CString-constants to int-constants.
enum IntKeyConst { CONST_ONE, CONST_TWO };
struct LookUpStruct {
int key;
CString value;
};
LookUpStruct lus[] = {
  { CONST_ONE, "CONST_ONE"},
  { CONST_TWO, "CONST_TWO"}
};

int findKey(const CString& val) {
   insigned int cnt = sizeof(lus)/sizeof(LookUpStruct);
   for(unsigned i=0; i < cnt; ++i) {
      if(val==lus[i].value) return lus[i].key;
   }
   return -1;
}

Then you could write:
val="CONST_ONE";

switch(findKey(val)){
case CONST_ONE:
break;
...
}
0
 
fsign21Commented:
You can not use CString in switch directly.
Recall the description of the switch-statement:

switch(expression){
   case constant_1:
      // code to execute if integer_val is val_1
      break;
    ...
   case constant_n:
      // code to execute if integer_val is val_n
      break;
   default:
      // code to execute if integer_val is none of the above
}
Note, that "expression" must evaluate to an integer type and that the "constant_s" must be integer constants (which include chars).

You have to use if() - else if() - else statements.

-----

Alternatively, you could program some kind of mapping of CString-constants to int-constants.
enum IntKeyConst { CONST_ONE, CONST_TWO };
struct LookUpStruct {
int key;
CString value;
};
LookUpStruct lus[] = {
  { CONST_ONE, "CONST_ONE"},
  { CONST_TWO, "CONST_TWO"}
};

int findKey(const CString& val) {
   insigned int cnt = sizeof(lus)/sizeof(LookUpStruct);
   for(unsigned i=0; i < cnt; ++i) {
      if(val==lus[i].value) return lus[i].key;
   }
   return -1;
}

Then you could write:
val="CONST_ONE";

switch(findKey(val)){
case CONST_ONE:
break;
...
}
0
 
grg99Commented:
As others have said, you cant use strings directly in a switch statement.

But you can map the strings to integers, with some clever code like this:

Cmds = "cmd1|cmd2|cmd3...";

Index = strstr( Cmds, SwitchVar );

if( Index < 0 ) { // Switchvar not in list }
else
{  switch(  Index / 5 + 1) {
case 1:  cmd1(); break;
case 2:  cmd2(); break; ......

}

0
 
SalteCommented:
switch case with string variables aren't directly possible in C and C++. You must somehow convert that string to an integer.

Using a hash function can get you a long way but beware of two strings that computes the same hash value.

switch (hashfunc(stringvar)) {
case HASH_VALUE_1:
   //....string is a string that computes to HASH_VALUE_1
   break;
case HASH_VALUE_2:
   // ...string is a string that computes to HASH_VALUE_2
   break;
}


If the hash function is designed so that for any two strings a and b that you might provide as value for stringvar and hashfunc(a) == hashfunc(b) implies that strcmp(a,b) == 0 then this method above works fine.

If it doesn't you must check explicitely for the strings in the case, so:

case HASH_VALUE_1:
   if (strcmp(stringvar,"string_1") == 0) {
      // string is string_1.
   } else {
      // string is some other string but it also
      // computes to HASH_VALUE_1.
   }
   break;

The other method is to 'intern' the string into a hash table. This is so that you guarantee that if two strings a and b are such that strcmp(a,b) == 0 then a == b.

const char * intern(char * a)
{
   const char * p = find_in_hash_table(a);
   if (p == 0) {
       char * q = strdup(a);
       insert_into_hash_table(q);
       p = q;
   }
   return p;
}

This function looks up the string in the hash table and if the string is already in the table you get the string that is there, if not you allocate a new string and insert it into the hash table and then return that newly allocated string.

Now you can do:

const char * a = intern("hello");
const char * b = intern("hello");

and a == b if and only if strcmp(a,b) == 0.

Now you can do switch:

switch(int(intern(stringvar))) {
case STRING_VAR_1:
   // string is "string_1" if and only if
   // int(intern("string_1")) is STRING_VAR_1.
   break;

You still have to provide constant values in the switch so you must do associate each interned string with a specific value that can be known at compile time. The value int(a) for a const char * a is constant enough for a specific value for a string a, but it is not known at compile time and must be stored in a variable. case labels must be constants and so you end up with something like this:

create a map between each string and associate an integer value with it, for example the string "a" is 1, while string "foo" is 2 and string "bar" is 3. Since these values are known before hand you get it like this:

map<string,int> m;
m["a"] = 1;
m["foo"] = 2;
etc...


then you can do something like this:

map<string,int>::iterator p = m.find(stringvar);

void do_something(const char * str)
{
   int v;

   if (p == m.end()) {
     // string not in map... set to a 'default' value, a
     // value not used in any case label).
     v = SOME_DIFFERENT_VALUE;
   } else {
     v = *p;
   }
   switch (v) {
   case 1: // string is "a"
      ...
      break;
   case 2: // string is "foo"
      ...
      break;
   default: // string is something else.
      ....
      break;
   }
}

Note that this uses a map instead of an explicit hash function. This is better since you can then assign the values without regard for what the hashfunction compute the value to be.

C# allow strings in switch statements and they use a method similar to the 'intern' method shown above. However, in C# the compiler translates a case label like:

case "foo":

to something like, compute intern("foo") and then use the returned value from that intern call as the case label when it builds up the switch table.

The intern call isn't actually done explicitely since it is done with all string literals in C# and it is done when the literal is loaded into memory from the .exe file so the switch with strings is essentially almost as cheap as a switch with int values. It is therefore good practice to use strings in switches in C# even though it looks horrible to most C and C++ programmers.

Alf
0
 
fsign21Commented:
grg99,

be careful because your solution has a limitation, that all string constants have to be of the same length.

For example, it does not work in case
Cmds = "cmd1|cmd2|cmd3|...|cmd10|cmd11";
or
Cmds = "one|two|three|four|five|six";
0
 
grg99Commented:
>be careful because your solution has a limitation, that >all string constants have to be of the same length.

>For example, it does not work in case
>Cmds = "cmd1|cmd2|cmd3|...|cmd10|cmd11";
>or
>Cmds = "one|two|three|four|five|six";

You're right, BUT:
Not a problem if you soup-up the strings like this:

Cmds = "1|cmd1|2|commandtwo|3|commandthatisthree"...

p = strstr( Line, Cmds );
if( p > 0 ) p = Line[p-2];

It's also a good idea to add |delimiters| around the comamnd you're searching for, unless you do want a partial match.





0
 
fsign21Commented:
grg99,
I see the same problem if
Cmds = "1|cmd1|2|whatever|...|10|ten";
because of p = Line[p-2];

It's also a good idea to test you solution with more than 9 entries and different strings...
0
 
SalteCommented:
Actually if you have that no string is contain a | character you can do it like that, just have to make the function a tad more general:

int ifroms(const char * s, const char * t)
{
   // t is a string of the form "a|num|b|num|...|num"
   // s is compared to the strings a, b, c etc until
   // a match is found, if a match is found the number
   // after is return, otherwise if no string matches
   // 0 is returned. (if any case has 0 as return value
   // that is their problem.
   const char * p = strchr(t,'|');
   while (p != 0) {
      size_t len = p++ - t;
      if (strncmp(s,t,len) == 0 && s[len] == 0) // match.
         return strtol(p,0,0);
      t = strchr(p,'|'); // find | after number.
      if (t == 0)
         return 0; // no | after!
      p = strchr(++t,'|');
   }
   return 0;
}

Then you can do:

   switch(ifroms(str,"hello|1|there|2")) {
   case 1:
       // str == "hello"
       break;
   case 2:
      // str == "there"
      break;
   default:
      // other string.
   }


Note that setting "foo|0|bar|1" will cause 'foo' to be treated as 'default' case.

This permit the value after the string to be any value.

switch (ifroms(str,"hello|125378|there|0x3fff")) {
case 125378:
   // str == "hello"
case 0x3fff:
   // str == "there"
};

if you want to, you can have a little test for '\'' there and read the char value if you want:

I.e. replace the:

         return strtol(p,0,0);

with:

   if (*p != '\'')
      return strtol(p,0,0);
   else if (*++p != '\\')
      return *p;
   else {
      switch (*p) {
      case 'n': return '\n';
      case 't': return '\t';
      etc etc...
      case 'x':
         // '\xXX'
         return strtol(p+1,0,16);
      default:
         if (*p >= '0' && *p <= '7')
            return strtol(p,0,8);
         return *p;
      }
   }

and you can even do things like:

switch (ifroms(str,"hello|'a'|there|12768|foo|0x37|bar|0177")) {
...

with the obvious interpretation.

If the string becomes very long it might be a good idea to break it:

switch (ifroms(str,"hello|'a'|"
                   "there|12768|"
                   "foo|0x37|"
                   "bar|0177")) {
case 'a': ....

now you could even include enum values in there but that would be more fancy with table lookup and everything, your imagination is the limit essentially.

Alf
0
 
grg99Commented:

>I see the same problem if
>Cmds = "1|cmd1|2|whatever|...|10|ten";
>because of p = Line[p-2];


Funny, I don't see the problem.  Neither do my programs that have been running with this method for the last, oh, 28 yrs.

Regards,


grg99

0
 
SalteCommented:
The problem is that setting p = p - 2 means you move it twice back, so if you're looking for "foo" and you found it in "....|13|foo|...." the p will point at the "f" and move backwards once it will point to "|foo" and back one more it will move to "3|foo" and this is then taken as value 3 if you do *p but this isn't 3 it is 13!

To do it right you could do a:

 --p;
 while ( *--p != '|');
 switch(atoi(p+1)) {
 ...

This indicates that the very first number must also have a | in front, so:

"|1|...|2|...|3|etc..."

is the correct format of the string.

Alf
0
 
fsign21Commented:
Salte, this is exactly what I ment.

Another thing: there is a limitation on string's content.
You can not store strings like "1","2","5" etc.  
0
 
SalteCommented:
fsign21,

Don't worry, grg will probably get around to understand it one of these days too...you'll never know :-)

It's possible he never used more than 10 cases in those 28 years. Or that although he has more than 10 cases but those above 9 are never used and he never notice :-)

It's also possible that he does have more than 10 cases andt his code is written so that he can take care of it but he just never notice at this moment.

We'll never know.

Alf
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 5
  • 5
  • 3
  • +4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now