How to Get enum item name from its value

C++Enums

C++ Problem Overview


I declared a enum type as this,

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

How can I get the item name "Mon, Tue, etc" when I already have the item value "0, 1, etc."

I already have a function as this

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

And now I want the output below

> Today is Wed, enjoy

C++ Solutions


Solution 1 - C++

You can't directly, enum in C++ are not like Java enums.

The usual approach is to create a std::map<WeekEnum,std::string>.

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

Solution 2 - C++

Here is another neat trick to define enum using 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;
}

Live Demo: https://ideone.com/bPAVTM

Outputs:

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

Solution 3 - C++

No, you have no way to get the "name" from the value in C++ because all the symbols are discarded during compilation.

You may need this way X Macros

Solution 4 - C++

An enumeration is something of an inverse-array. What I believe you want is this:

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.

Solution 5 - C++

You can define an operator that performs the output.

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

Solution 6 - C++

> How can I get the item name "Mon, Tue, etc" when I already have the > item value "0, 1, etc."

On some older C code (quite some time ago), I found code analogous to:

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;
}

Con: This establishes a "pathological dependency" between the enumeration values and the function... meaning if you change the enum you must change the function to match. I suppose this is true even for a std::map.

I vaguely remember we found a utility to generate the function code from the enum code. The enum table length had grown to several hundred ... and at some point it is maybe a sound choice to write code to write code.


Note -

in an embedded system enhancement effort, my team replaced many tables (100+?) of null-terminated-strings used to map enum int values to their text strings.

The problem with the tables was that a value out of range was often not noticed because many of these tables were gathered into one region of code / memory, such that a value out-of-range reached past the named table end(s) and returned a null-terminated-string from some subsequent table.

Using the function-with-switch statement also allowed us to add an assert in the default clause of the switch. The asserts found several more coding errors during test, and our asserts were tied into a static-ram-system-log our field techs could search.

Solution 7 - C++

On GCC it may look like this:

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

Solution 8 - C++

May not be the best solution ever, but this does the job pretty well.

The names of the enums are lazy loaded, so after the first call to to_string it will be loaded and kept in memory.

The method from_string was not implemented as it was not requested here, but it can be easily implemented by calling get_enum_names, searching the name in the vector, and casting its position to the enum type.

Please add the definition of get_enum_names in a cpp file (only the declaration should be in the header file).

It should work fine with C++ >= 11
Tested in gcc and MSVC

Implementation:

#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)]; \
    }

Usage:

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;
}

Solution 9 - C++

I have had excellent success with a technique which resembles the X macros pointed to by @RolandXu. We made heavy use of the stringize operator, too. The technique mitigates the maintenance nightmare when you have an application domain where items appear both as strings and as numerical tokens.

It comes in particularily handy when machine readable documentation is available so that the macro X(...) lines can be auto-generated. A new documentation would immediately result in a consistent program update covering the strings, enums and the dictionaries translating between them in both directions. (We were dealing with PCL6 tokens).

And while the preprocessor code looks pretty ugly, all those technicalities can be hidden in the header files which never have to be touched again, and neither do the source files. Everything is type safe. The only thing that changes is a text file containing all the X(...) lines, and that is possibly auto generated.

Solution 10 - C++

If you know the actual enum labels correlated to their values, you can use containers and C++17's std::string_view to quickly access values and their string representations with the [ ] operator while tracking it yourself. std::string_view will only allocate memory when created. They can also be designated with static constexpr if you want them available at run-time for more performance savings. This little console app should be fairly fast.

#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;
}

Output:

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

Solution 11 - C++

The solution I prefer is to mix arrays and ostream like this:

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;

I also suggest to use enum class instead of enum.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNano HEView Question on Stackoverflow
Solution 1 - C++Luchian GrigoreView Answer on Stackoverflow
Solution 2 - C++Killzone KidView Answer on Stackoverflow
Solution 3 - C++RolandXuView Answer on Stackoverflow
Solution 4 - C++CosineView Answer on Stackoverflow
Solution 5 - C++PuppyView Answer on Stackoverflow
Solution 6 - C++2785528View Answer on Stackoverflow
Solution 7 - C++rick-rick-rickView Answer on Stackoverflow
Solution 8 - C++rflobaoView Answer on Stackoverflow
Solution 9 - C++Peter - Reinstate MonicaView Answer on Stackoverflow
Solution 10 - C++kayleeFrye_onDeckView Answer on Stackoverflow
Solution 11 - C++Elvis DukajView Answer on Stackoverflow