How can I compare strings in C using a `switch` statement?

Viewed 317580

In C there is a switch construct which enables one to execute different conditional branches of code based on an test integer value, e.g.,

int a;
/* Read the value of "a" from some source, e.g. user input */
switch (a) {
  case 100:
    // Code
    break;
  case 200:
    // Code
    break;
  default:
    // Code
    break;
}

How is it possible to obtain the same behavior (i.e. avoid the so-called "if-else ladder") for a string value, i.e., a char *?

16 Answers

We cannot escape if-else ladder in order to compare a string with others. Even regular switch-case is also an if-else ladder (for integers) internally. We might only want to simulate the switch-case for string, but can never replace if-else ladder. The best of the algorithms for string comparison cannot escape from using strcmp function. Means to compare character by character until an unmatch is found. So using if-else ladder and strcmp are inevitable.

DEMO

And here are simplest macros to simulate the switch-case for strings.

#ifndef SWITCH_CASE_INIT
#define SWITCH_CASE_INIT
    #define SWITCH(X)   for (char* __switch_p__ = X, int __switch_next__=1 ; __switch_p__ ; __switch_p__=0, __switch_next__=1) { {
    #define CASE(X)         } if (!__switch_next__ || !(__switch_next__ = strcmp(__switch_p__, X))) {
    #define DEFAULT         } {
    #define END         }}
#endif

And you can use them as

char* str = "def";

SWITCH (str)
    CASE ("abc")
        printf ("in abc\n");
        break;
    CASE ("def")              // Notice: 'break;' statement missing so the control rolls through subsequent CASE's until DEFAULT 
        printf("in def\n");
    CASE ("ghi")
        printf ("in ghi\n");
    DEFAULT
        printf("in DEFAULT\n");
END

Output:

in def
in ghi
in DEFAULT

Below is nested SWITCH usage:

char* str = "def";
char* str1 = "xyz";

SWITCH (str)
    CASE ("abc")
        printf ("in abc\n");
        break;
    CASE ("def")                                
        printf("in def\n");
        SWITCH (str1)                           // <== Notice: Nested SWITCH
            CASE ("uvw")
                printf("in def => uvw\n");
                break;
            CASE ("xyz")
                printf("in def => xyz\n");
                break;
            DEFAULT
                printf("in def => DEFAULT\n");
        END
    CASE ("ghi")
        printf ("in ghi\n");
    DEFAULT
        printf("in DEFAULT\n");
END

Output:

in def
in def => xyz
in ghi
in DEFAULT

Here is reverse string SWITCH, where in you can use a variable (rather than a constant) in CASE clause:

char* str2 = "def";
char* str3 = "ghi";

SWITCH ("ghi")                      // <== Notice: Use of variables and reverse string SWITCH.
    CASE (str1)
        printf ("in str1\n");
        break;
    CASE (str2)                     
        printf ("in str2\n");
        break;
    CASE (str3)                     
        printf ("in str3\n");
        break;
    DEFAULT
        printf("in DEFAULT\n");
END

Output:

in str3

Function pointers are a great way to do this, e.g.

result = switchFunction(someStringKey); //result is an optional return value

...this calls a function that you have set by string key (one function per case):

setSwitchFunction("foo", fooFunc);
setSwitchFunction("bar", barFunc);

Use a pre-existing hashmap/table/dictionary implementation such as khash, return that pointer to a function inside of switchFunction(), and execute it (or just return it from switchFunction() and execute it yourself). If the map implementation doesn't store that, just use a uint64_t instead that you cast accordingly to a pointer.

Comparing using an if () else if () chain is a linear search, offering O(n) time complexity. For a large number of strings, one option is using bsearch() (binary search) to achieve O(log n) time complexity. Below is an example of my use of bsearch to perform a given action based on an input string. (Specifically, my example finds a given mathematical function by name).

static int c(const void *const restrict a, const void *const restrict b) {
    return strcmp(*(const char *const *)a, *(const char *const *)b);
}
static double (*func(const char *const str))(double) {
    static const char *const s[] = {"abs", "acos", "acosh", "asin", "asinh", "atan", "atanh", "cbrt", "cos", "cosh", "ln", "log", "sin", "sinh", "sqrt", "tan", "tanh"};
    static double (*const f[])(double) = {fabs, acos, acosh, asin, asinh, atan, atanh, cbrt, cos, cosh, log, log10, sin, sinh, sqrt, tan, tanh};
    const char *const *const r = bsearch(&str, s, sizeof(s)/sizeof(*s), sizeof(*s), c);
    return r ? f[r-s] : NULL;
}
Related