将enum和string组合成一个类似map的变量,可以同时用于int和string

C++ Combine enum and string into a map-like variable, able to be used with both int and string

本文关键字:string 变量 int 用于 map 组合 enum 一个      更新时间:2023-10-16

假设我有一个enum:

enum Types
{
    TYPE_ASCENDING,
    TYPE_DESCENDING
};

,我用它来…代码中的任何地方。比如if(bla < TYPE_ASCENDING),或者是switch/case。实际的enum要大得多。

无论检查结果(或其他任何结果)如何,它都需要以更美观的方式为std::cout,以便让用户知道发生了什么。那么,继续if()的例子,它可能是这样的:

if(bla < TYPE_ASCENDING)
    std::cout << "Ascending.n";

所有这些都发生在类中。我的问题:是否有一种方法来定义某种类型的变量/STL/任何允许存储enumstd::string类变量的东西,但也让我单独使用这两种类型?

一个想法是namespace,但它似乎不能在类中使用。为了举例说明,下面是它的样子:

namespace Type
{
    enum Types
    {
        ASCENDING,
        DESCENDING
    };
    std::string s[2] {"Ascending", "Descending"};
};

,对于if(),它被称为Type::ASCENDING,对于字符串,它被称为Type::s[0]。但是,在类中没有namespace,所以这不是一个解决方案。

使用std::map只允许我使用int作为索引,所以我只能使用这个:

std::map<Types, std::string> m {{TYPE_ASCENDING, "Ascending}, {TYPE_DESCENDING, "Descending"}};

作为m[0]m[TYPE_ASCENDING],但我不能调用它,因为它的索引要在if()中使用。为此,我必须单独调用enum,这意味着我有enummap两个变量。我需要一个统一的变量名,以避免在代码中追逐变量名。

如果我使用struct,我不能直接访问Struct::TYPE_DESENDING,我需要创建一个对象。

我可以使用enumstd::string数组/向量,但这意味着,再次,我必须分别调用两个变量,我希望它们是统一的。

我想要的可能吗?

在原生c++中并没有这种机制。你可以写一个map/mapper函数

enum class E
{
    ONE,
    TWO
};
std::unordered_map<E,std::string> eStrings { {E::ONE,"ONE"},{E::TWO,"two"}};

虽然这是c++ 11,但您可以对旧版本的c++做同样的操作

你可以这样写

std::cout << eStrings[E::ONE];

这里的问题是您必须手动维护它。因此,当向枚举中添加新值时,必须手动向映射中添加新条目。

对于编写具有此行为的类或函数也是如此。您总是必须复制枚举声明的代码和到字符串的映射。

这里的一个解决方案是使用一些工具来生成这些

你可以在一些文件中定义你的enum(这只是一些随机的格式,只是为了解释这一点)。在您自己的定义文件中选择您想要的任何内容)

E
- ONE
- TWO

然后在头文件和/或cpp文件中生成c++枚举和Map。

enum class <name>
{
    <foreach:> <value>,
};
std::unordered_map< <name> ,std::string> eStrings 
{ 
    <foreach:> {<name>::<value>,"<value>"},
};

如果你不喜欢使用映射,这种方法是非常灵活的。如果愿意,还可以生成一个switch case语句

std::string getString(<name> e)
{
    switch(e)
    {
        <foreach:> case <name>::<value>: return "<value>";
    }
}

这里的语法没有任何标准,只是一些"伪代码"来可视化这个概念。有几种方法可以生成c++代码。你可以选择任何你想要的,或者编写自己的程序。

注意:

这也是一个一般的概念。你可以将这个功能/映射等包装到另一个类中,使其静态等进行优化,而不是将其放在全局作用域中。


如果你需要比查找字符串的映射更花哨的东西,你可以用这个概念创建一个类,或者另一个只做反向查找的映射。更重要的是,您很可能不得不使用外部工具生成代码。

阅读Hayts的回答我看到我最初写的可能与自动生成代码方面有关。所以我把它留在这里。


看到常规的旧enum可以隐式地转换为int(而不是enum classes),您可以简单地使用map<int, string>

现在,到有趣的部分,半自动生成它。

#include <iostream>
#include <map>
#include <string>
struct Thing {
    enum Type {
#     define ENUM_DEF(v, s) v,
        ENUM_DEF(TYPE_ASCENDING,  "Ascending")
        ENUM_DEF(TYPE_DESCENDING, "Descending")
#     undef ENUM_DEF
    };
    std::map<int, std::string> string;
    Thing() {
#     define ENUM_DEF(v, s) string[v] = s;
        ENUM_DEF(TYPE_ASCENDING,  "Ascending")
        ENUM_DEF(TYPE_DESCENDING, "Descending")
#     undef ENUM_DEF
    }
};

int main() {
    Thing t;
    std::cout << t.string[0];
    return 0;
}

我使用了一种称为x -宏的技术。前提是将枚举所需的所有参数传递给宏。然后根据需要如何使用参数来定义宏。首先:

#   define ENUM_DEF(v, s) v,

这只是将enum标记扩展为常规enum定义。

然后,在Thing中c'tor:

#     define ENUM_DEF(v, s) string[v] = s;

展开为填充映射所需的语句。

最后一点,你可能有问题:你真的要做所有这些重复,重新输入ENUM_DEF所有的时间?
幸运的是,你不知道。你可以把这些语句移动到它们自己的文件中,我们把它命名为type_enum.def:

#ifdef ENUM_DEF
  ENUM_DEF(TYPE_ASCENDING,  "Ascending")
  ENUM_DEF(TYPE_DESCENDING, "Descending")
#endif //ENUM_DEF

原来的代码变成:

#include <iostream>
#include <map>
#include <string>
struct Thing {
    enum Type {
#   define ENUM_DEF(v, s) v,
#     include "type_enum.def"
#   undef ENUM_DEF
    };
    std::map<int, std::string> string;
    Thing() {
#     define ENUM_DEF(v, s) string[v] = s;
#       include "type_enum.def"
#     undef ENUM_DEF
    }
};

int main() {
    Thing t;
    std::cout << t.string[0];
    return 0;
}