如何从其值获取枚举项名称

问题描述 投票:0回答:12

我声明了一个枚举类型,

enum WeekEnum
{
Mon = 0;
Tue = 1;
Wed = 2;
Thu = 3;
Fri = 4;
Sat = 5;
Sun = 6;
};

当我已经拥有项目值“0、1等”时,如何获取项目名称“周一、周二等”

我已经有这样的功能了

Log(Today is "2", enjoy! );

现在我想要下面的输出

今天是周三,尽情享受吧

c++ enums
12个回答
50
投票

你不能直接,

enum
在C++中不像Java枚举。

通常的方法是创建一个

std::map<WeekEnum,std::string>

std::map<WeekEnum,std::string> m;
m[Mon] = "Monday";
//...
m[Sun] = "Sunday";

28
投票

这是使用 X Macro 定义枚举的另一个巧妙技巧:

#include <iostream>

#define WEEK_DAYS \
X(MON, "Monday", true) \
X(TUE, "Tuesday", true) \
X(WED, "Wednesday", true) \
X(THU, "Thursday", true) \
X(FRI, "Friday", true) \
X(SAT, "Saturday", false) \
X(SUN, "Sunday", false)

#define X(day, name, workday) day,
enum WeekDay : size_t
{
    WEEK_DAYS
};
#undef X

#define X(day, name, workday) name,
char const *weekday_name[] =
{
    WEEK_DAYS
};
#undef X

#define X(day, name, workday) workday,
bool weekday_workday[]
{
    WEEK_DAYS
};
#undef X

int main()
{
    std::cout << "Enum value: " << WeekDay::THU << std::endl;
    std::cout << "Name string: " << weekday_name[WeekDay::THU] << std::endl;
    std::cout << std::boolalpha << "Work day: " << weekday_workday[WeekDay::THU] << std::endl;

    WeekDay wd = SUN;
    std::cout << "Enum value: " << wd << std::endl;
    std::cout << "Name string: " << weekday_name[wd] << std::endl;
    std::cout << std::boolalpha << "Work day: " << weekday_workday[wd] << std::endl;

    return 0;
}

现场演示:https://ideone.com/bPAVTM

输出:

Enum value: 3
Name string: Thursday
Work day: true
Enum value: 6
Name string: Sunday
Work day: false

18
投票

不,你无法从 C++ 中的值中获取“名称”,因为所有符号在编译过程中都会被丢弃。

您可能需要这种方式X宏


9
投票

您可以定义执行输出的运算符。

std::ostream& operator<<(std::ostream& lhs, WeekEnum e) {
    switch(e) {
    case Monday: lhs << "Monday"; break;
    .. etc
    }
    return lhs;
}

8
投票

枚举是一种逆数组。我相信你想要的是这样的:

const char * Week[] = { "", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };  // The blank string at the beginning is so that Sunday is 1 instead of 0.
cout << "Today is " << Week[2] << ", enjoy!";  // Or whatever you'de like to do with it.

3
投票

在 GCC 上它可能看起来像这样:

const char* WeekEnumNames [] = {
    [Mon] = "Mon",
    [Tue] = "Tue",
    [Wed] = "Wed",
    [Thu] = "Thu",
    [Fri] = "Fri",
    [Sat] = "Sat",
    [Sun] = "Sun",
};

1
投票

当我已经有了项目名称“周一、周二等”时,如何获取项目名称 项目值“0、1等”

在一些较旧的 C 代码(很久以前),我发现代码类似于:

std::string weekEnumToStr(int n)
{
   std::string s("unknown");
   switch (n)
   {
   case 0: { s = "Mon"; } break;
   case 1: { s = "Tue"; } break;
   case 2: { s = "Wed"; } break;
   case 3: { s = "Thu"; } break;
   case 4: { s = "Fri"; } break;
   case 5: { s = "Sat"; } break;
   case 6: { s = "Sun"; } break;
   }
   return s;
}

缺点:这在枚举值和函数之间建立了“病态依赖性”...这意味着如果更改枚举,则必须更改函数以匹配。我想即使对于 std::map 也是如此。

我依稀记得我们找到了一个实用程序来从枚举代码生成函数代码。枚举表的长度已经增长到了几百……在某些时候,编写代码来编写代码可能是一个明智的选择。


注意 -

在嵌入式系统增强工作中,我的团队替换了许多用于将枚举 int 值映射到其文本字符串的空终止字符串表(100+?)。

表的问题是,超出范围的值通常不会被注意到,因为许多表都被收集到代码/内存的一个区域中,因此超出范围的值超出了指定的表末尾并从某个后续表中返回一个以空字符结尾的字符串。

使用 function-with-switch 语句还允许我们在 switch 的 default 子句中添加断言。断言在测试期间发现了更多编码错误,并且我们的断言被绑定到我们的现场技术人员可以搜索的静态内存系统日志中。


1
投票

可能不是最好的解决方案,但这很好地完成了工作。

枚举的名称是延迟加载的,因此在第一次调用

to_string
后,它将被加载并保存在内存中。

方法

from_string
未实现,因为这里没有要求它,但可以通过调用
get_enum_names
、在向量中搜索名称并将其位置转换为枚举类型来轻松实现。

请在cpp文件中添加

get_enum_names
的定义(只需在头文件中声明)。

它应该可以与 C++ >= 11 一起正常工作
在 gcc 和 MSVC 中测试

实施:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <unordered_map>

// Add the definition of this method into a cpp file. (only the declaration in the header)
const std::vector<std::string>& get_enum_names(const std::string& en_key, const std::string& en_str)
{
    static std::unordered_map<std::string, std::vector<std::string>> en_names_map;
    const auto it = en_names_map.find(en_key);
    if (it != en_names_map.end())
        return it->second;

    constexpr auto delim(',');
    std::vector<std::string> en_names;
    std::size_t start{};
    auto end = en_str.find(delim);
    while (end != std::string::npos)
    {
        while (en_str[start] == ' ')
            ++start;
        en_names.push_back(en_str.substr(start, end - start));
        start = end + 1;
        end = en_str.find(delim, start);
    }
    while (en_str[start] == ' ')
        ++start;
    en_names.push_back(en_str.substr(start));
    return en_names_map.emplace(en_key, std::move(en_names)).first->second;
}

#define DECLARE_ENUM(ENUM_NAME, ...)   \
    enum class ENUM_NAME{ __VA_ARGS__ }; \
    inline std::string to_string(ENUM_NAME en) \
    { \
        const auto& names = get_enum_names(#ENUM_NAME #__VA_ARGS__, #__VA_ARGS__); \
        return names[static_cast<std::size_t>(en)]; \
    }

用途:

DECLARE_ENUM(WeekEnum, Mon, Tue, Wed, Thu, Fri, Sat, Sun);

int main()
{
    WeekEnum weekDay = WeekEnum::Wed;
    std::cout << to_string(weekDay) << std::endl; // prints Wed
    std::cout << to_string(WeekEnum::Sat) << std::endl; // prints Sat
    return 0;
}

0
投票

我使用类似于 @RolandXu 指出的 X 宏的技术取得了巨大的成功。我们也大量使用了 stringize 运算符。当您的应用程序域中的项目既显示为字符串又显示为数字标记时,该技术可以减轻维护噩梦。

当机器可读的文档可用时,它会特别方便,以便可以自动生成宏 X(...) 行。新的文档将立即导致一致的程序更新,涵盖字符串、枚举和它们之间双向转换的字典。 (我们正在处理 PCL6 代币)。

虽然预处理器代码看起来相当丑陋,但所有这些技术细节都可以隐藏在头文件中,永远不必再次触及,源文件也一样。一切都是类型安全的。唯一改变的是包含所有 X(...) 行的文本文件,并且可能是自动生成的。


0
投票

如果您知道与其值相关的实际枚举标签,则可以使用容器和 C++17 的

std::string_view
通过
[
]
运算符快速访问值及其字符串表示形式,同时自行跟踪。
std::string_view
只会在创建时分配内存。如果您希望它们在运行时可用以节省更多性能,也可以用
static constexpr
指定它们。这个小控制台应用程序应该相当快。

#include <iostream>
#include <string_view>
#include <tuple>    
int main() {
    enum class Weekdays { //default behavior starts at 0 and iterates by 1 per entry
        Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
    };

    static constexpr std::string_view Monday    = "Monday";
    static constexpr std::string_view Tuesday   = "Tuesday";
    static constexpr std::string_view Wednesday = "Wednesday";
    static constexpr std::string_view Thursday  = "Thursday";
    static constexpr std::string_view Friday    = "Friday";
    static constexpr std::string_view Saturday  = "Saturday";
    static constexpr std::string_view Sunday    = "Sunday";
    static constexpr std::string_view opener    = "enum[";
    static constexpr std::string_view closer    = "] is ";
    static constexpr std::string_view semi      = ":";

    std::pair<Weekdays, std::string_view> Weekdays_List[] = {
        std::make_pair(Weekdays::Monday,    Monday),
        std::make_pair(Weekdays::Tuesday,   Tuesday),
        std::make_pair(Weekdays::Wednesday, Wednesday),
        std::make_pair(Weekdays::Thursday,  Thursday),
        std::make_pair(Weekdays::Friday,    Friday),
        std::make_pair(Weekdays::Saturday,  Saturday),
        std::make_pair(Weekdays::Sunday,    Sunday)
    };

    for (int i=0;i<sizeof(Weekdays_List)/sizeof(Weekdays_List[0]);i++) {
        std::cout<<opener<<i<<closer<<Weekdays_List[(int)i].second<<semi\
        <<(int)Weekdays_List[(int)i].first<<std::endl;
    }    
    return 0;
}

输出:

enum[0] is Monday:0
enum[1] is Tuesday:1
enum[2] is Wednesday:2
enum[3] is Thursday:3
enum[4] is Friday:4
enum[5] is Saturday:5
enum[6] is Sunday:6

0
投票

我更喜欢的解决方案是像这样混合数组和ostream:

std::ostream& operator<<(std::ostream& lhs, WeekEnum e) {
    static const std::array<std::string, 7> WEEK_STRINGS = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };

    return lhs << WEEK_STRINGS[static_cast<WeekEnum>(e)]
}

cout << "Today is " << WeekEnum::Monday;

我还建议使用

enum class
而不是
enum


0
投票

如果我不需要枚举的标称值,我找到了一个很好的解决方案,就是创建一个带有常量的类


using string = std::string;

class Color {

public:
    string RED = "RED";

© www.soinside.com 2019 - 2024. All rights reserved.