14

Is there a convienent way to take a string (input by user) and convert it to an Enumeration value? In this case, the string would be the name of the enumeration value, like so:

enum Day
{
    Sunday = 0,
    Monday = 1,
    ...
}

So that if the user gave the name of a Day, it would be able to parse that to the corresponding Enum value.

The trick is, I have over 500 values I'm working with, and they are spread out across multiple enumerations.

I know of the Enum.Parse Method in c#, so is there some form of this in c?

11
  • 3
    How do you want to relate strings and enumerations? Commented May 30, 2013 at 19:51
  • the string would be the name of the enumeration value. Commented May 30, 2013 at 19:54
  • 1
    Short answer: no, there's no "convenient" way, but you can probably get what you want through a tricky use of macros. See stackoverflow.com/questions/147267/… Commented May 30, 2013 at 19:58
  • 1
    or stackoverflow.com/questions/201593/… Commented May 30, 2013 at 20:00
  • Do you really have to use straight C? std::map<std::string, Day> would be ideal for this. Commented May 30, 2013 at 20:17

6 Answers 6

23

The standard way to implement it is something along the lines of:

typedef enum {value1, value2, value3, (...) } VALUE;

const static struct {
    VALUE      val;
    const char *str;
} conversion [] = {
    {value1, "value1"},
    {value2, "value2"},
    {value3, "value3"},
       (...)
};

VALUE
str2enum (const char *str)
{
     int j;
     for (j = 0;  j < sizeof (conversion) / sizeof (conversion[0]);  ++j)
         if (!strcmp (str, conversion[j].str))
             return conversion[j].val;    
     error_message ("no such string");
}

The converse should be apparent.

Sign up to request clarification or add additional context in comments.

6 Comments

honestly, I'm a little lost, can you clarify what this does a bit?
@Nealon: It sets up a data structure which associates each string value with a corresponding enum value. The conversion function looks for a string match and returns the corresponding enum value.
so its like making a dictionary in C#?
Maybe const char *str; instead of char *str;. Compiler might detect some wrong uses during this development.
It's worth noting that this algorithm is O(n) on the number of possible enum values. If you're really just doing the days of the week, this will be fine, but if you have an enum with e.g. 100 possible values, and you're calling this function a large number of times, it might add up.
|
4

Warning, this is a total hack. You can use dlsym to do a lookup of a variable that is appropriately initialized. For this example to work, you have to compile to allow local symbols to be visible to the dynamic linker. With GCC, the option is -rdynamic.

enum Day {
    SunDay, MonDay, TuesDay, WednesDay, ThursDay, FriDay, SaturDay
};

enum Day Sunday = SunDay,
         Monday = MonDay,
         Tuesday = TuesDay,
         Wednesday = WednesDay,
         Thursday = ThursDay,
         Friday = FriDay,
         Saturday = SaturDay;

int main () {
    const char *daystr = "Thursday";
    void *h = dlopen(0, RTLD_NOW);
    enum Day *day = dlsym(h, daystr);
    if (day) printf("%s = %d\n", daystr, *day);
    else printf("%s not found\n", daystr);
    return 0;
}

1 Comment

Daring! But could come in handy when I feel lazy.
4

There isn't a direct way, but with C, you improvise. Here's an old trick. Purists may balk at this. But it's a way to manage this kind of stuff somewhat sanely. Uses some preprocessor tricks.

In constants.h put in the following:

CONSTANT(Sunday,  0)
CONSTANT(Monday,  1)
CONSTANT(Tuesday, 2)

In main.c:

#include <stdio.h>

#define CONSTANT(name, value) \
    name = value,

typedef enum {
    #include "constants.h"
} Constants;

#undef CONSTANT

#define CONSTANT(name, value) \
    #name,

char* constants[] = {
    #include "constants.h"
};  

Constants str2enum(char* name) {
    int ii;
    for (ii = 0; ii < sizeof(constants) / sizeof(constants[0]); ++ii) {
        if (!strcmp(name, constants[ii])) {
            return (Constants)ii;
        }   
    }   
    return (Constants)-1;
}   

int main() {
    printf("%s = %d\n", "Monday", str2enum("Monday"));
    printf("%s = %d\n", "Tuesday", str2enum("Tuesday"));
    return 0;
}

You can try other variations of the basic idea.

6 Comments

+1. As for a variation, if you use a struct to associate the string to the enum value, you could qsort() the array, and use bsearch() to do the lookup.
Whenever someone mentions "preprocessor tricks", I usually grit my teeth because such associated code is usually challenging to read and very challenging to debug. I understand the appeal of this kind of thing, but have pity for those who have to maintain it.
I do like it, but the enumerations are predefined and I'm not allowed to modify them.
Yes, the appeal of this approach is in actually being able to define a real enum, and an array of strings, that automatically keep in sync. Otherwise, just use answer by @wallyk. But having this technique in your toolbox can be useful - in spite of what wallyk says above :) It actually makes things more clear and maintainable (once you understand what's going on). And there are other possibilities. Like expanding the include file in the context of a switch statement, to implement dispatchers etc. Kind of like code generation.
@wallyk My post went down before I had seen yours, no worries, no judgments were made.
|
1

Not really, though if you use a hash function you can setup all of the values of your enum to match a set of hashed strings. You might have to use a more complicated hash if you don't care about case-sensitivity.

This is probably your best solution, since it has lower overhead than strcmp (...). The assignment of an enum value from a string hash does not require repeated string comparisons, etc...

1 Comment

using a hash would definitely be the least expensive option, and judging from the scale of this, I think that will be the way to go. I'll have to generate separate hash functions for each enumeration.
1

If you're using straight C, there isnt a "Enum.Parse" equivalent. You'll want to write your own function, comparing the user's string to pre-defined values with strcmp(), and then returning the appropriate enum value.

Another possibility is using an existing "hash map" implementation, or rolling your own - for instance, the one in glib should work for you: https://developer.gnome.org/glib/2.30/glib-Hash-Tables.html

A hash map should be faster than doing a linear search on the possible enum values, if you have a lot of them (for instance, if you were doing something other than the days of the week). A good hash map implementation should be close to O(1) for lookups, instead of O(n) for a linear search.

1 Comment

I have 500+ plus values across multiple enumerations I'm working with. I'll update my question to reflect that.
0

That would be a good solution :

enum e_test { a, b, c, END };

enum e_test get_enum_value(char * val) {
    static char const * e_test_str[] = { "a", "b", "c" };
    for (int i = 0; i < END; ++i)
        if (!strcmp(e_test_str[i], val))
            return i;
    return END;
 }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.