按名称获取枚举值

6

我有一个枚举,其中包含数百个条目。

我将以字符串形式获取枚举的值。是否有办法将字符串转换为枚举值?否则,我将不得不使用数百个if语句。

考虑

enum Colors { Red, Green, Blue, Yellow ... } there are more than 100 entries

我将在一个字符串变量中得到"Red"

String color = "Red"; // "Red" would be generated dynamically.

通常我们通过以下方式访问枚举值:Colors::RedColors::Blue等等... 是否有一种方式可以像这样访问它:

Colors::color; // i.e enumtype::stringVariable

在很多帖子中,都提到了我们可以使用map,但是在构建map时,我们最终会使用数百个if语句。

有没有什么方法可以避免这种情况呢?


3
不,不是那样的。而且,你所谈论的并不是C语言。 - Ry-
避免这种情况的方法是使用实际的表格。 - Ry-
实际上,这个表格是指考虑了C/C++两种语言的情况。 - Gokul Kulkarni
1
好的,一个简单的方法是将每个名称放入一个数组中,假设它们是从0开始连续的。 - Ry-
5
你的问题被标记为C语言,但是在C语言中Colors::color是一个语法错误,在C语言中也没有预定义的名为String的类型。你正在使用什么编程语言?是C#吗? - Keith Thompson
显示剩余4条评论
4个回答

8
这里介绍一种用C语言实现的方法,类似于Paddy的C++ map。该宏保证了名称和相应的枚举值之间的联系。
enum Colors { NoColor, Red, Green, Blue, Yellow };

enum Colors get_color(const char *s)
{
    const struct {
        char *name;
        enum Colors color;
    } colormap[] = {
#define Color(x) {#x, x}
        Color(Red),
        Color(Green),
        Color(Blue),
        Color(Yellow)
#undef Color
    };
    for (size_t i = 0; i < sizeof colormap / sizeof colormap[0]; ++i) {
        if (!strcmp(s, colormap[i].name)) {
            return colormap[i].color;
        }
    }
    return NoColor;
}


编辑 如@sh1在评论中建议的那样(现已消失),您可以使用X宏来定义颜色列表。这避免了两次定义列表。以下是使用X-macro重写上面示例的方法-感谢sh1的提示:

#define COLORS  X(Red), X(Green), X(Blue), X(Yellow),

enum Colors {
    NoColor, 
#define X(x) x
    COLORS
#undef X
};

enum Colors get_color(const char *s)
{
    const struct {
        char *name;
        enum Colors color;
    } colormap[] = {
#define X(x) {#x, x}
        COLORS
#undef X
    };
...etc

喜欢这个解决方案!在 Color(Yellow) 语句后面立即取消定义 Color(x) 是否是一个好主意,以防止代码的其他部分产生意外结果? - Floris

5
使用X-macro技术。几乎直接从维基百科转录:
#define LIST_OF_COLORS \
    X(Red) \
    X(Green) \
    X(Blue) \
    X(Yellow)

#define X(name) name,
enum Colors { LIST_OF_COLORS };
#undef X

#define X(name) #name,
char const * const ColorName[] = { LIST_OF_COLORS };
#undef X

由于枚举自动分配从零开始的值,并且当我们创建名称数组时无法意外重复列表,因此使用枚举作为索引进入ColorName数组将始终直接指向相应的单词,在映射该方向时无需搜索。所以:
printf("%s\n", ColorName[Red]);

将打印:
Red

另一方面,进行以下操作:
enum Color strtoColor(char const *name)
{
    for (int i = 0; i < sizeof(ColorName) / sizeof(*ColorName); i++)
        if (strcmp(ColorName[i], name) == 0)
            return (enum Color)i;
    return -1;
}

编辑

如果您正在使用C ++,则可以使用Paddy答案中的X宏:

static std::map<string, enum Colors> colorMap;
void InitColorMap()
{
#define X(name) colorMap[#name] = name;
    LIST_OF_COLORS
#undef X
}

或者,从this answer借鉴,在C++11中:

static std::map<string, enum Colors> colorMap =
{
#define X(name) { #name, name },
    LIST_OF_COLORS
#undef X
};

...或其他什么。那不是我的语言。


我意识到更有用的方法是使用 #define LIST_OF_FOO(X) X(foo1) X(foo2)...,因为这样你可以实例化像 LIST_OF_FOO(STRINGIZE) 这样的东西,并且只需为多个列表定义一次 STRINGIZE - sh1
刚刚确认了我的先前评论是合法的,而且这个问题说明了如何在有多个枚举需要描述时获得更紧凑的代码。我应该编辑这个答案,但我怀疑我会找到时间。 - sh1

0
这有一点技巧性,但如果你使用纯C语言,你可以这样做:
char* stringArray[]={"Red", "Green", "Blue", "Yellow"};

然后你可以通过循环数组来查找匹配的字符串,直到找到为止:

for(ii = 0; ii < size(stringArray); ii++) {
  if (strcmp(inputString, stringArray[ii]) == 0) {
    // you found it!
  }
}

0

不清楚你在这里使用的是C还是C++。

解决这个问题的一种方法是使用带有字符串化操作符的宏。类似这样:

#define RegisterColor(c) colorMap[#c] = (c)

在这种情况下,我假设类似于C++中的map
enum Colors { .... };
static std::map<string, enum Colors> colorMap;

void InitColorMap()
{
    RegisterColor(Red);
    RegisterColor(Green);
    RegisterColor(Blue);
    RegisterColor(Yellow);
    // ...
}

有一些重复,但如果你需要使用enum,那就无法避免。

在C语言中,使用自己的数据结构也很容易实现同样的原理。


1
如果您正在使用 std::map,根本没有必要涉及预处理器,只需将它们设置为 colorMap["Red"] = Red; - Ed S.
1
当然可以,但现在“Red”出现了三次而不是两次。 - paddy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接