tags:

views:

299

answers:

7

I just wonder how can I switch on the string "B1" ? Don't ask why this string :-)

+9  A: 

If you mean, how to write something similar to this:

switch (string) {
  case "B1": 
    // do something
    break;
  /* more case "xxx" parts */
}

Then the canonical solution in C is to use an if-else ladder:

if (strcmp(string, "B1") == 0) 
{
  // do something
} 
else if (strcmp(string "xxx") == 0)
{
  // do something else
}
/* more else if clauses */
else /* default: */
{
}
Bart van Ingen Schenau
Actually, the problem is that I already have a switch on int and in a special case I have the value "B1" and "B2" which I want to use in same switch. The Only way is to somehow convert the "B1" and "B2" values and use them as int!!?
Niklas
@Niklas: This is important information for your question. Can you update your question and explain (if possible with some (pseudo-)code) what you are trying to do?
Bart van Ingen Schenau
@Niklas: You should clarify your question: how on earth could "B1" and "B2" be a special case of an int?
Edgar Bonet
@Niklas: if you need to make decisions based on different data types, then you will have to change your control structure. The easiest and most straightforward is to use an if-else chain. Alternately, instead of switching on a variable, you can switch on a function that will take an `int` and `char *` as arguments and return an `int` value based on those inputs.
John Bode
Edgar: Maybe he means 0xB1 and 0xB2?
Eyal
Gooosh, this is what I mean. I have these defines (NOOO, I can't change these defines)
Niklas
#define A 1#define B 2#define C S1#define D S2and these values are what I want to use in my switch. So simple :-)
Niklas
@Niklas: Defines are not strings. If the define is for a number, you can use it directly in your switch like this `switch (something) { case A: /*...*/ break; case B: /*...*/ break; }`.
Bart van Ingen Schenau
+4  A: 

Hi Niklas

To add to Phimueme's answer above, if your string is always two characters, then you can build a 16-bit int out of the two 8-bit characters - and switch on that (to avoid nested switch/case statements).

Mike

MikeBrom
can you explain it a little bit more!? thx.
Niklas
@Mike: If you really wish to `To add to Phimueme's answer above`, then feel free to use the comment function. :)
Onion-Knight
@Onion: You'll note that MikeBrom does not currently have the reputation to comment on posts other than his own and answers to his own questions. That said, @Mike "above" is slippery in SO, becuase there is no reliable sort-order. Better to link to the answer like *"... [in Phimueme's answer](http://stackoverflow.com/questions/4014827/best-way-to-switch-on-a-string-in-c/4014887#4014887) ..."* (though that answer is deleted now, and the link is only good for user with 10k+ reputation).
dmckee
+6  A: 

If you have many cases and do not want to write a ton of strcmp() calls, you could do something like:

switch(my_hash_function(the_string)) {
    case HASH_B1: ...
    /* ...etc... */
}

You just have to make sure your hash function has no collisions inside the set of possible values for the string.

Edgar Bonet
"make sure your hash function has no collisions inside the set of possible values for the string." -- Does such a hash function exists for the alphabet `[a-zA-Z0-9_]`? Any example?
ArunSaha
@ArunSaha: Obviously not for _arbitrary combinations_ of such characters.
Edgar Bonet
A: 

I think the best way to do this is separate the 'recognition' from the functionality:

struct stringcase { char* string; void (*func)(void); };

void funcB1();
void funcAzA();

stringcase cases [] = 
{ { "B1", funcB1 }
, { "AzA", funcAzA }
};

void myswitch( char* token ) {
  for( stringcases* pCase = cases
     ; pCase != cases + sizeof( cases ) / sizeof( cases[0] )
     ; pCase++ )
  {
    if( 0 == strcmp( pCase->string, token ) ) {
       (*pCase->func)();
       break;
    }
  }

}
xtofl
+3  A: 

There is no way to do this in C. There are a lot of different approaches. Typically the simplest is to define a set of constants that represent your strings and do a look up by string on to get the constant:

#define BADKEY -1
#define A1 1
#define A2 2
#define B1 3
#define B2 4

typedef struct { char *key; int val; } t_symstruct;

static t_symstruct lookuptable[] = {
    { "A1", A1 }, { "A2", A2 }, { "B1", B1 }, { "B2", B2 }
};

#define NKEYS (sizeof(lookuptable)/sizeof(t_symstruct))

int keyfromstring(char *key)
{
    int i;
    for (i=0; i < NKEYS; i++) {
        t_symstruct *sym = lookuptable + i;
        if (strcmp(sym->key, key) == 0)
            return sym->val;
    }
    return BADKEY;
}

/* ... */
switch (keyfromstring(somestring)) {
case A1: /* ... */ break;
case A2: /* ... */ break;
case B1: /* ... */ break;
case B2: /* ... */ break;
case BADKEY: /* handle failed lookup */
}

There are, of course, more efficient ways to do this. If you keep your keys sorted, you can use a binary search. You could use a hashtable too. These things change your performance at the expense of maintenance.

plinth
A: 

Assuming little endianness and sizeof(char) == 1, you could do that (something like this was suggested by MikeBrom).

char* txt = "B1";
int tst = *(int*)txt;
if ((tst & 0x00FFFFFF) == '1B')
    printf("B1!\n");

It could be generalized for BE case.

ruslik
Dont do that! This may cause a "data alignment" exception. It's not guaranteed that the char* txt points to an address, that matches alignment requirements of int.
harper
-1 for invoking UB with ugly hacks.
R..
@R he asked for that. @harper it's not the case for x86.
ruslik
@ruslik, Niklas didn't ask for x86. And since you mentioned the big endian case, you don't address exclusively the x86 environment. So that'
harper
A: 

This is how you do it. No, not really.

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <arpa/inet.h>

main (void)
{
    uint32_t s[0x40]; 
    assert((unsigned char)1 == (unsigned char)(257));
    memset(s, 0, sizeof(s));
    fgets((char*)s, sizeof(s), stdin);

    switch (ntohl(s[0])) {
        case 'open':
        case 'read':
        case 'seek':
            puts("ok");
            break;
        case 'rm\n\0':
            puts("not authorized");
            break;
        default:
            puts("unrecognized command");  
    }
    return 0;
}
jbcreix