How do I 'ToString()' an enum in C++?
In Java and C# I would just call ToString.
enum Colours
{
Red =0,
Green=1,
Blue=2
};
I need to create a string like: "Invalid colour '" + colour + "' selected."
How do I 'ToString()' an enum in C++?
In Java and C# I would just call ToString.
enum Colours
{
Red =0,
Green=1,
Blue=2
};
I need to create a string like: "Invalid colour '" + colour + "' selected."
While this is commonly done through switches, I prefer arrays:
#include <iostream>
namespace foo {
enum Colors { BLUE = 0, RED, GREEN, SIZE_OF_ENUM };
static const char* ColorNames[] = { "blue", "red", "green" };
// statically check that the size of ColorNames fits the number of Colors
static_assert(sizeof(foo::ColorNames)/sizeof(char*) == foo::SIZE_OF_ENUM
, "sizes dont match");
} // foo
int main()
{
std::cout << foo::ColorNames[foo::BLUE] << std::endl;
return 0;
}
The explicit array size has the benefit of generating a compile time error should the size of the enum change and you forget to add the appropriate string.
Alternatively, there is Boost.Enum in the Boost vault. The library hasn't been officially released but is quite stable and provides what you want. I wouldn't recommend it to a novice though.
How about a little magic with macros:
#include <iostream>
#include <string>
#include <vector>
// http://stackoverflow.com/questions/236129/how-to-split-a-string-in-c
std::vector<std::string> split(const std::string &text, char sep) {
std::vector<std::string> tokens;
int start = 0, end = 0;
while ((end = text.find(sep, start)) != std::string::npos) {
tokens.push_back(text.substr(start, end - start));
start = end + 1;
}
tokens.push_back(text.substr(start));
return tokens;
}
#define ENUM(name, ...)\
enum name \
{\
__VA_ARGS__\
};\
std::vector<std::string> name##Map = split(#__VA_ARGS__, ',');\
std::string toString(const name v) { return name##Map.at(v);}
ENUM(Color, Red,Green,Blue)
int main(int c, char**v)
{
std::cout << toString(Red) << toString(Blue);
return 0;//a.exec();
}
Yes, I understand that this is ugly and you'd better not do do such things
That's inherently impossible.
A C++ enum is just a set of numbers with compile-time names.
At runtime, they are indistinguishable from ordinary numbers.
You need to write a switch
statement that returns a string.
I really like the macro approach of @Lol4t0.
I extended it to be able to convert an enum from a string too:
#include <iostream>
#include <string>
#include <vector>
// http://stackoverflow.com/questions/236129/how-to-split-a-string-in-c
std::vector<std::string> split(const std::string &text, char sep) {
std::vector<std::string> tokens;
int start = 0, end = 0;
while ((end = text.find(sep, start)) != std::string::npos) {
tokens.push_back(text.substr(start, end - start));
start = end + 1;
}
tokens.push_back(text.substr(start));
return tokens;
}
#define ENUM(name, ...)\
enum name\
{\
__VA_ARGS__\
};\
static const int name##Size = (sizeof((int[]){__VA_ARGS__})/sizeof(int));\
static const vector<string> name##ToStringMap = split(#__VA_ARGS__, ',');\
const string name##ToString(const name value)\
{\
return name##ToStringMap.at(value);\
};\
map<string, name> name##ToFromStringMap(...)\
{\
map<string, name> m;\
name args[name##Size] = { __VA_ARGS__ };\
\
int i;\
for(i = 0; i < name##Size; ++i)\
{\
m[name##ToString(args[i])] = args[i];\
}\
return m;\
};\
static map<string, name> name##FromStringMap = name##ToFromStringMap(__VA_ARGS__);\
const name name##FromString(const string value, const name defaultValue)\
{\
if(name##FromStringMap.count(value) == 0)\
{\
return defaultValue;\
}\
return name##FromStringMap[value];\
};
Usage:
ENUM(MyEnum, Value1, Value2)
void main()
{
string valueName = MyEnumToString(MyEnum::Value2);
MyEnum value = MyEnumFromString(valueName, MyEnum::Value1);
}
I am not a C++ expert, so let me know what you think or how to do better.
enum Color
{
Red =0,
Green=1,
Blue=2
};
std::string ColorMap[] = { "Red", "Green","Blue" };
Use ColorMap[c]
to get the string representation:
std::string msg = "Invalid colour '" + ColorMap[c] + "' selected.";
However, if the values of enum are not continuous, then you can use std::map
instead as:
enum Color
{
Red = 0x1,
Green = 0x2,
Blue = 0x4,
Black = 0x8,
};
//C++11 only, as it uses std::initializer_list
std::map<Color, std::string> ColorMap = {
{Red, "Red"},
{Green, "Green"},
{Blue, "Blue"},
{Black, "Black"}
};
//same as before!
std::string msg = "Invalid colour '" + ColorMap[c] + "' selected.";
You have to do it manually, i.e.
const char* ToString(Colours co) {
switch(co) {
case Red:
return "Red";
// ...
}
}
A lookup table would also be possible. I've also seen people using custom scripts to generate such stuff on top of their source code.
You can store the names in an array of strings, indexed by the enum
values.
enum Colours
{
Red =0,
Green=1,
Blue=2
};
char* names[3] = {"Red", "Green", "Blue"};
Then you can print: "Invalid colour '" + names[colour] + "' selected."
But this approach may not be very useful if you do not define the enum
values sequentially. In that case this approach will waste memory. Writing a function with a switch
over the enum
value would be useful, as Alexander Gessler has mentioned. Another alternative may be the map
from STL.
As @FlopCoder said:
enum Colours
{
Red =0,
Green=1,
Blue=2
};
char* ColourNames[] = { "Red", "Green", "Blue" };
int colour = Green;
printf( "Invalid colour '%s' selected.", ColourNames[ colour ] );
This of course will only work if your enum starts at 0 and is continuous.
@Nawaz's way is more C++ stylish though.