Here are some enum classes:
enum class Race : char {AINU, ELF, DWARF, MAN, EAGLE, HOBBIT, ENT, ORC, WIZARD};
enum class Color: char {RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE};
enum class Direction: char{UP, DOWN, LEFT, RIGHT};
I would like to implement an enum_to_string function and a string_to_enum function for each.
It's no problem converting enums into strings, because I can overload the same function name.
std::string to_string(Race const& enum_value);
std::string to_string(Color const& enum_value);
std::string to_string(Direction const& enum_value);
However, you can't overload the same way when converting to enums, because only the return-type would be different. (nor would I want to, because different enums may be represented by the same string.)
Are one of the following ways possible for converting strings into enums?
Race race = to_enum<Race>("elf");
Color color = to_enum<Color>("green");
std::string blah{"up"};
Direction dir{to_enum<Direction>(blah)};
or possibly:
Race race = to_enum(Race,"elf");
Color color = to_enum(Color,"green");
std::string blah{"up"};
Direction dir{to_enum(Direction,blah)};
Can C++ support one or both of these behaviours?
I'm trying to avoid different function names like this:
Race to_race(std::string const& str);
Color to_color(std::string const& str);
Direction to_direction(std::string const& str);
Here is the closest thing I could come up with,
template <typename T>struct to_enum{};
template <>
struct to_enum<Color>{
static Color convert(std::string const& str){
//match the string with a color enum, and return that color enum
//(or like a default color enum if the string is garbage or something)
}
};
and then you call it like this:
Color color = to_enum<Color>::convert("red");
can we get rid of the convert? or possibly implement this one?
Color color = to_enum(Color,"red");
Use function template and specialization. These are rare situations where I think function specialization really helps.
template<typename TEnum>
TEnum enum_cast(std::string const & s);
then specialize it for each enum type. I changed the name, so you can read it like cast:
Color color = enum_cast<Color>("blue");
Or parse
is also a good name. Choose whatever you think is good. I would personally choose either parse
or enum_cast
.
Now for to_string
, you can define overloads (which you already have):
std::string to_string(Color c);
std::string to_string(Race r);
std::string to_string(Direction d);
Don't use template and specialization for to_string
. Overloads are indeed good in general.
and then you call it like this:
Color color = to_enum<Color>::convert("red");
can we get rid of the convert?
Sure thing! Simply encapsulate the call:
template <typename Enum>
Enum to_enum(std::string const& from) {
return to_enum_helper<Enum>::convert(from);
}
Where to_enum_helper
is the struct you defined previously. (EDIT: Or you specialise the function directly as shown in Nawaz’ answer.)
Alternatively, you can use overloading instead of specialisation if that’s your cup of tea:
Race to_enum_helper(std::string const& from, Race) {
// Implement for enum class Race
}
Color to_enum_helper(std::string const& from, Color) {
// Implement for enum class Color
}
template <typename Enum>
Enum to_enum(std::string const& from) {
return to_enum_helper(from, Enum());
}
Here, the second argument to to_enum_helper
is simply used to distinguish the different overloads.
However, you can't overload the same way when converting to enums, because only the return-type would be different.
You could overload conversion operators to write overload functions that differ only by return type:
#include <string>
#include <iostream>
enum Race {};
enum Color {};
struct to_one_of_them {
operator Race () const { std::cout << "to Race()\n"; return Race(); }
operator Color () const { std::cout << "to Color()\n"; return Color(); }
to_one_of_them() = delete;
private:
std::string str;
to_one_of_them(std::string const &str) : str(str) {}
friend to_one_of_them to_enum(std::string const&);
};
to_one_of_them to_enum(std::string const &str) {
return {str};
}
int main () {
Race r = to_enum("meh");
Color c = to_enum("meh");
}
However, this technique is virtually unused and therefore not a C++ idiom and thus probably not worth to learn.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With