Is there a simple way to convert C++ enum to string?
Solution 1
You may want to check out GCCXML.
Running GCCXML on your sample code produces:
<GCC_XML>
<Namespace id="_1" name="::" members="_3 " mangled="_Z2::"/>
<Namespace id="_2" name="std" context="_1" members="" mangled="_Z3std"/>
<Enumeration id="_3" name="MyEnum" context="_1" location="f0:1" file="f0" line="1">
<EnumValue name="FOO" init="0"/>
<EnumValue name="BAR" init="80"/>
</Enumeration>
<File id="f0" name="my_enum.h"/>
</GCC_XML>
You could use any language you prefer to pull out the Enumeration and EnumValue tags and generate your desired code.
Solution 2
X-macros are the best solution. Example:
#include <iostream>
enum Colours {
# define X(a) a,
# include "colours.def"
# undef X
ColoursCount
};
char const* const colours_str[] = {
# define X(a) #a,
# include "colours.def"
# undef X
0
};
std::ostream& operator<<(std::ostream& os, enum Colours c)
{
if (c >= ColoursCount || c < 0) return os << "???";
return os << colours_str[c];
}
int main()
{
std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl;
}
colours.def:
X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)
However, I usually prefer the following method, so that it's possible to tweak the string a bit.
#define X(a, b) a,
#define X(a, b) b,
X(Red, "red")
X(Green, "green")
// etc.
Solution 3
@hydroo: Without the extra file:
#define SOME_ENUM(DO) \
DO(Foo) \
DO(Bar) \
DO(Baz)
#define MAKE_ENUM(VAR) VAR,
enum MetaSyntacticVariable{
SOME_ENUM(MAKE_ENUM)
};
#define MAKE_STRINGS(VAR) #VAR,
const char* const MetaSyntacticVariableNames[] = {
SOME_ENUM(MAKE_STRINGS)
};
Solution 4
What I tend to do is create a C array with the names in the same order and position as the enum values.
eg.
enum colours { red, green, blue };
const char *colour_names[] = { "red", "green", "blue" };
then you can use the array in places where you want a human-readable value, eg
colours mycolour = red;
cout << "the colour is" << colour_names[mycolour];
You could experiment a little with the stringizing operator (see # in your preprocessor reference) that will do what you want, in some circumstances- eg:
#define printword(XX) cout << #XX;
printword(red);
will print "red" to stdout. Unfortunately it won't work for a variable (as you'll get the variable name printed out)
Solution 5
I have an incredibly simple to use macro that does this in a completely DRY fashion. It involves variadic macros and some simple parsing magic. Here goes:
#define AWESOME_MAKE_ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT}; \
inline std::ostream& operator<<(std::ostream& os, name value) { \
std::string enumName = #name; \
std::string str = #__VA_ARGS__; \
int len = str.length(); \
std::vector<std::string> strings; \
std::ostringstream temp; \
for(int i = 0; i < len; i ++) { \
if(isspace(str[i])) continue; \
else if(str[i] == ',') { \
strings.push_back(temp.str()); \
temp.str(std::string());\
} \
else temp<< str[i]; \
} \
strings.push_back(temp.str()); \
os << enumName << "::" << strings[static_cast<int>(value)]; \
return os;}
To use this in your code, simply do:
AWESOME_MAKE_ENUM(Animal,
DOG,
CAT,
HORSE
);
Edu Felipe
A bitter/sour software developer who hates IDEs and loves VIM.
Updated on July 08, 2022Comments
-
Edu Felipe almost 2 years
Suppose we have some named enums:
enum MyEnum { FOO, BAR = 0x50 };
What I googled for is a script (any language) that scans all the headers in my project and generates a header with one function per enum.
char* enum_to_string(MyEnum t);
And a implementation with something like this:
char* enum_to_string(MyEnum t){ switch(t){ case FOO: return "FOO"; case BAR: return "BAR"; default: return "INVALID ENUM"; } }
The gotcha is really with typedefed enums, and unnamed C style enums. Does anybody know something for this?
EDIT: The solution should not modify my source, except for the generated functions. The enums are in an API, so using the solutions proposed until now is just not an option.
-
Ronny Brendel over 15 yearsnifty, although I don't like the extra file
-
xtofl over 15 yearsJust make sure your build process doesn't prepend #pragma( once ) before every include file...
-
Ankit Roy about 15 years+1, GCCXML looks very nice! (Although I almost -1ed as I initially misread this as a suggestion to use the above verbose XML syntax to encode your enum -- a solution which reeks of overengineering!)
-
phillipwei over 14 yearsany change you can post the python script?
-
Edu Felipe over 14 yearsThe problem with this solution is that it changes the header defining the enum, which I cannot do. So this is not enough for my problem.
-
Edu Felipe almost 14 yearsCould you please put this on a repository somewhere (github, google code or bitbucket) and post the link here, instead of mediafire? I would help people wanting to understand it :)
-
Jonathan Graehl over 13 yearsIt's perfectly legal. I do it all the time.
-
Lightness Races in Orbit about 13 yearsI'm not sure about "best" solution!
-
Adam Bruss over 12 yearsGood solution. This is c++ so using stl map is ok.
-
Bruno Martinez over 11 yearsI love this solution. It would be clearer if SOME_UNION and MAKE_UNION were called SOME_ENUM and MAKE_ENUM, though.
-
Deqing over 10 yearsGood solution. But for me I'd prefer
switch and case
as it is simple and easy to understand. -
Julien Guertault over 10 yearsThis solution is just vastly superior to any switch case or array based one, because it doesn't duplicate the names, making it easy to change the enumeration.
-
ikku100 about 10 yearsOne note: there is a hash missing in the last piece of example code. It should be: #define X(a, b) a, #define X(a, b) #b, And the first line could also be: #define X(a) a,
-
learnvst about 10 years@ikku100 you are incorrect about
#define X(a, b) #b
. This is only necessary if the definition looks like thisX(Red, red)
, rather than the definition shown in the answer,X(Red, "red")
-
Alok about 10 yearsKindly explain why this is the answer.
-
Spooky almost 10 yearsThis does not answer the OP's question: he was looking for a way to automatically generate a function to return an enum's member's name as a string.
-
DCurro almost 10 yearsThis is a great solution. I have the most maintainable C++ resource manager that I've ever dealt with.
-
FractalSpace over 9 yearsAwesome! @RonnyBrendel, a small tweak can make it a single-file solution (see my answer below)
-
Vincent over 9 yearsYou and user3360260 have a good solution. Why not having a multimap instead?
-
yamifm0f over 9 yearsI must thank you for this simple solution :-) - I did modify it a bit though, to have the
MetaSyntacticVariableNames[]
be part of a class declaration, by making a methodstatic const char* getNameByEnum(MetaSyntacticVariable e) { /*code to return the static string*/ }
-
chappjc over 8 yearsGood idea using a strongly typed enum (enum class). Here's a demo: cpp.sh/4ife
-
chappjc over 8 yearsThe last caveat (won't work for a variable) is a big drawback, but +1 anyway.
-
Jason Harrison over 8 yearsDoes this work with externally defined enumerations/symbols. For example, OS defined or library defined symbols with gaps in the numbering?
-
AlwaysLearning over 8 yearsVery nice, but does not compile if put inside a class (I could not figure out why).
-
srcspider almost 8 yearsActually this is fairly useless, since the stringify method is at compile time and is quite literal. If you say have the enum type in question inside a variable, attempting to stringify the variable will just give you the variable name, not the enum type name.
-
463035818_is_not_a_number almost 7 yearsseems like suma moved the answer here. You may want to include the link in your answer. I only found the comment by chance and witout sumas answer this one is rather pointless
-
Craig.Feied over 6 yearsI could not get this to compile in VS2015. I get a warning and an error: warning: multi-line comment [-Wcomment] #define MAKE_ENUM(name, ...) enum class name { VA_ARGS, __COUNT} error: stray '#' in program std*: string enumName = #name
-
kyb over 6 yearsWorks only if you won't set special numeric values to enum entries.
-
Th. Thielemann over 6 yearsHere is an online generator: th-thielemann.de/tools/cpp-enum-to-string.html
-
Francois Bertrand over 5 yearsFantastic answer! I simplified it further by grouping MAKE_ENUM and MAKE_STRINGS into a single macro, making the whole process even simpler. I added an answer in this thread with that code if anyone is interested.
-
Philippe Carphin almost 4 yearsI think this could also be adapted with
#define X(a) case a: return #a;
andchar *color_str(Colours c){ switch(c){ #define, #include, #undef, default: return "error"; }
and this way it would handle enums with non-sequential integer values. -
Sz. over 2 yearsNice, but it kinda seems practical for diagnostics only. BTW, apropos principles (like "DRY"):
cout << Animal::CAT
should still just print1
(rather thanAnimal::CAT
), as per the principle of least surprise. :) -
Milan over 2 yearsCan someone please explain what's going on in this solution? I still don't understand. eg, if I have
enum class MyClr {R, G, B};
outsidemain
function then what should I modify in the above code and where should I put it? I put my enum class at the very top then replacedSOME_ENUM
s withMyClr
andFoo
,Bar
,Baz
withR
,G
,B
respectively. But, inmain
, when I try to run,MyClr clr= MyClr::R; cout << MAKE_STRINGS(clr);
, I get an error:error: expected primary-expression before ‘<<’ token cout << MAKE_STRINGS(clr); ^~
-
Andrew about 2 yearsYou have to declare each member? What a complete waste!