【问题标题】:Get enum value by name按名称获取枚举值
【发布时间】:2013-08-06 02:12:21
【问题描述】:

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

我将获取枚举值作为字符串。有没有办法将字符串转换为枚举值?否则,我最终会使用数百个 if 语句。

考虑

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

我将在字符串变量中获得"Red"

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

通常我们通过以下方式访问枚举, Colors::Red, Colors::Blue 等等... 有什么方法可以让我们以这样的方式访问它:

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

在这里的许多帖子中,我们都可以使用 map,但是在构建 map 时,我们最终会使用数百个 ifs。

有什么办法可以避免吗?

【问题讨论】:

  • 不,不是那样的。另外,你说的不是 C。
  • 避免这种情况的方法是使用实​​际的表格。
  • 某种意义上的实际表??考虑到 c/cpp。
  • 嗯,一种简单的方法是将每个名称放入一个数组中,假设它们从 0 开始是连续的。
  • 您的问题被标记为 C,但 Colors::color 是 C 中的语法错误,并且没有名为 String 的预定义类型。您使用什么语言?是 C# 吗?

标签: c enums


【解决方案1】:

这是一种 C 方式,类似于 Paddy 的 C++ 映射。宏保证名称和对应的枚举绑定在一起。

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 宏重写的上述示例 - 感谢 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) 以防止代码的其他部分出现意外结果是个好主意吗?
【解决方案2】:

使用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++,那么,使用 X-macro on paddy 的回答:

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

或者,在 C++11 中从 this answer 窃取:

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
  • 只是confirmed 我之前的评论是合法的,这个问题说明了当你有多个枚举要描述时如何获得更紧凑的代码。我应该编辑这个答案,但我怀疑我会找到时间。
【解决方案3】:

这有点小技巧,但如果您使用纯 C,您可以执行以下操作:

char* stringArray[]={"Red", "Green", "Blue", "Yellow"};

然后你可以通过循环遍历数组直到找到匹配的字符串来找到匹配的字符串:

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

【讨论】:

    【解决方案4】:

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

    解决这个问题的一种方法是使用带有 stringize 运算符的宏。像这样的:

    #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 中使用相同的原理很容易。

    【讨论】:

    • 如果您使用的是std::map,则根本没有理由涉及预处理器,只需将它们设置为colorMap["Red"] = Red;
    • 当然,但现在“红色”出现三次而不是两次。
    猜你喜欢
    • 2016-12-07
    • 2013-04-08
    • 2012-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-21
    • 1970-01-01
    • 2021-07-25
    相关资源
    最近更新 更多