Converting enum to string and vice versa

It is working:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <iostream>
#include <string>
#include <vector>
#include <map>

const int ENUM_NOT_FOUND = -1;  const std::string NEW = "  ";
enum Day {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};

template <typename Enum>
class EnumConversions;

template <typename Enum>
struct EnumManager {
    static std::string toString (const Enum en);
    static Enum toEnum (const std::string& str);
    static const std::map<Enum, std::string> enumToStringMap;
    static const std::map<std::string, Enum> stringToEnumMap;
private:
    static std::map<Enum, std::string> initializeEnumToStringMap();
    static std::map<std::string, Enum> initializeStringToEnumMap();
};

template <typename Enum>
const std::map<Enum, std::string> EnumManager<Enum>::enumToStringMap =  EnumManager<Enum>::initializeEnumToStringMap();

template<typename Enum>
std::map<Enum, std::string> EnumManager<Enum>::initializeEnumToStringMap() {
    std::map<Enum, std::string> m;
    for (const auto& x: EnumConversions<Enum>::enumToStringVector)
        m[x.first] = x.second;
    return m;
}

template <typename Enum>
const std::map<std::string, Enum> EnumManager<Enum>::stringToEnumMap =  EnumManager<Enum>::initializeStringToEnumMap();

template<typename Enum>
std::map<std::string, Enum> EnumManager<Enum>::initializeStringToEnumMap() {
    std::map<std::string, Enum> m;
    for (const auto& x: EnumConversions<Enum>::enumToStringVector)
        m[x.second] = x.first;
    return m;
}

template <typename Enum>
std::string EnumManager<Enum>::toString (const Enum en) {
    auto it = EnumManager<Enum>::enumToStringMap.find (en);  // std::map::find is the fastest lookup method
    if (it != EnumManager<Enum>::enumToStringMap.end())
        return it->second;
    return "[[[Enum to string not found." + NEW + "Programmer made an error]]]";
}

template<typename Enum>
Enum EnumManager<Enum>::toEnum (const std::string& str) {
    auto it = EnumManager<Enum>::stringToEnumMap.find (str);
    if (it != EnumManager<Enum>::stringToEnumMap.end())
        return it->second;
    return static_cast<Enum> (ENUM_NOT_FOUND);
}

template <typename Enum>
class EnumConversions : public EnumManager<Enum> {
	private:
	    EnumConversions();  // to prevent instantiation
	public:
	    static const std::vector<std::pair<Enum, std::string>> enumToStringVector;
};

template <>
const std::vector<std::pair<Day, std::string>> EnumConversions<Day>::enumToStringVector = { 
	{Monday, "Monday"}, {Tuesday, "Tuesday"}, {Wednesday, "Wednesday"}, {Thursday, "Thursday"}, {Friday, "Friday"}, {Saturday, "Saturday"}, {Sunday, "Sunday"} 
};

std::ostream& operator << (std::ostream& os, Day day) {  
    return os << EnumConversions<Day>::toString (day);
}

std::istream& operator >> (std::istream& is, Day& day) {
    std::string buf;
    is >> buf;
    day = EnumConversions<Day>::toEnum (buf);
    return is;
}

int main () {
	for (int DAY = Monday; DAY <= Sunday; DAY++)
		std::cout << static_cast<Day> (DAY) << std::endl;
	Day day;
	std::cout << "Name a day: ";
	while (true)
	{
		std::cin >> day;
		std::cout << "day = " << day << std::endl;
	}
}


Ouput with GCC 4.8.1:
1
2
3
4
5
6
7
8
9
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
Name a day: Friday
day = Friday


But the problem is that whenever I define a new enum, I have to define the << and >> overloads for the new enum again. Isn't there a way to template that as well, so that the << and >> overload needs to be defined just once? My atttempt:
1
2
3
4
5
6
7
8
9
10
11
12
template<typename Enum>
std::ostream& operator << (std::ostream& os, Enum en) {
    return os << EnumConversions<Enum>::toString (en);
}

template<typename Enum>
std::istream& operator >> (std::istream& is, Enum& en) {
    std::string buf;
    is >> buf;
    en = EnumConversions<Enum>::toEnum (buf);
    return is;
}

fails to compile. I guess the problem is Enum is not known at compile time, even though it should be deducible during run time? Error mentions ambiguous overload for operator>>. What's the solution here?
Last edited on
Limit your I/O operator templates to types that actually are enums. Right now those templates match all types, which causes the ambiguity.
So I have no choice but to do what I'm already doing--define the overloads separately for each new enum, right?
The required fix should be very short, and my entire code above can be copied to check it. Could I have an explicit solution code?

I tried your suggestion. But
1
2
3
4
5
6
7
8
#include <type_traits>
// ...
template<typename Enum>
std::ostream& operator << (std::ostream& os, Enum en) {
    if (!std::is_enum<Enum>::value)
	return os << en;
    return os << EnumConversions<Enum>::toString (en);
}

still gives the same ambiguity error. So I see what you are saying. If Enum is not an actual enum, then use the standard <<. If Enum is an actual enum, then do the special EnumConversion<Enum>::toString thing, but the above still gives the same error.
Last edited on
Try this
1
2
3
4
5
6
7
8
9
10
11
12
template<typename Enum, class = typename std::enable_if<std::is_enum<Enum>::value>::type>
std::ostream& operator << (std::ostream& os, Enum en) {
    return os << EnumConversions<Enum>::toString(en);
}

template<typename Enum, class = typename std::enable_if<std::is_enum<Enum>::value>::type>
std::istream& operator >> (std::istream& is, Enum& en) {
    std::string buf;
    is >> buf;
    en = EnumConversions<Enum>::toEnum(buf);
    return is;
}
Ah! Bingo! Wasn't familar with std::enable_if. Thanks!
Topic archived. No new replies allowed.