Macro for doing a switch on strings

Viewed 68

Outside of using a hash function, I'd like to write a basic mapping of a key/value lookup of a few movie-credits related items. Does the following macro seem like an acceptable way to do it?

#include <stdio.h>
#include <string.h>

#define TOMAP(input_str, output_buf, from, to)  \
    if (strcmp(input_str, from) == 0) \
        strcpy(output_buf, to)

void map_imdb_position(const char* pos, char output[])
{
    TOMAP(pos, output, "Director", "director");
    TOMAP(pos, output, "Writer", "scribe");
}

int main(void) {
    char position[20];
    map_imdb_position("Director", position);
    printf("Director->%s\n", position);
}

If not, what might be a better approach to doing a sort of switch statement on a string comparison?

3 Answers

As suggested in the comments a cleaner way to do this would be with a map of credits in a function, for example:

#include <stdio.h>
#include <string.h>

typedef struct map {
    char from[20];
    char to[20];
} Map;

void map_imdb_position(const char* credit, char output[])
{
    static Map credits[] = {
        {"Director", "director"}, 
        {"Writer", "scribe"}
    };
    for (int i=0; i < sizeof(credits)/sizeof(*credits); i++) {
        if (strcmp(credit,credits[i].from) == 0) {
            strcpy(output, credits[i].to);
            break;
        }
    }
    printf("%s --> %s\n", credit, output);
}

int main(void) {
    char position[20];
    map_imdb_position("Director", position);
    map_imdb_position("Writer", position);
}

Working example: https://onlinegdb.com/Sk_t76aQ_

Side note: I was working on this solution but Carl already came up with a similar one.

But, I prefer to use the ptr->field syntax and to have an end-of-table sentinel at the end (vs. the sizeof construct to get the count).

And, I think designated initializers when constructing the table make things more readable [especially if more fields need to be added to the struct].

So, here's my version:

#include <stdio.h>
#include <string.h>

typedef struct {
    const char *from;
    const char *to;
} mapimdb_t;

mapimdb_t mapimdb[] = {
    { .from = "Director", .to = "director" },
    { .from = "Writer", .to = "scribe" },
    // ...
    { .from = NULL }
};

void
map_imdb_position(const char *pos, char *output)
{

    for (const mapimdb_t *map = mapimdb;  map->from != NULL;  ++map) {
        if (strcmp(pos,map->from) == 0) {
            strcpy(output,map->to);
            break;
        }
    }
}

void
dotest(const char *from,char *out)
{

    map_imdb_position(from,out);
    printf("dotest: %s->%s\n",from,out);
}

int
main(void)
{
    char position[20];

    dotest("Director", position);
    dotest("Writer", position);
}

X macros can be used:

#include <stdio.h>
#include <string.h>

#define STR_LIST \
     X("Director", "director") \
     X("Writer", "scribe")


void map_imdb_position(const char* pos, char output[])
{
#define X(from, to) \
    if (strcmp(pos, from) == 0) \
        { strcpy(output, to); return; }  // "return" was missing?
STR_LIST
#undef X
}

int main(void) {
    char position[20];
    map_imdb_position("Director", position);
    printf("Director->%s\n", position);
}
Related