Is there a simple way to convert C++ enum to string?

138,304

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
);
Share:
138,304
Edu Felipe
Author by

Edu Felipe

A bitter/sour software developer who hates IDEs and loves VIM.

Updated on July 08, 2022

Comments

  • Edu Felipe
    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
    Ronny Brendel over 15 years
    nifty, although I don't like the extra file
  • xtofl
    xtofl over 15 years
    Just make sure your build process doesn't prepend #pragma( once ) before every include file...
  • Ankit Roy
    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
    phillipwei over 14 years
    any change you can post the python script?
  • Edu Felipe
    Edu Felipe over 14 years
    The 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
    Edu Felipe almost 14 years
    Could 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
    Jonathan Graehl over 13 years
    It's perfectly legal. I do it all the time.
  • Lightness Races in Orbit
    Lightness Races in Orbit about 13 years
    I'm not sure about "best" solution!
  • Adam Bruss
    Adam Bruss over 12 years
    Good solution. This is c++ so using stl map is ok.
  • Bruno Martinez
    Bruno Martinez over 11 years
    I love this solution. It would be clearer if SOME_UNION and MAKE_UNION were called SOME_ENUM and MAKE_ENUM, though.
  • Deqing
    Deqing over 10 years
    Good solution. But for me I'd prefer switch and case as it is simple and easy to understand.
  • Julien Guertault
    Julien Guertault over 10 years
    This 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
    ikku100 about 10 years
    One 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
    learnvst about 10 years
    @ikku100 you are incorrect about #define X(a, b) #b. This is only necessary if the definition looks like this X(Red, red), rather than the definition shown in the answer, X(Red, "red")
  • Alok
    Alok about 10 years
    Kindly explain why this is the answer.
  • Spooky
    Spooky almost 10 years
    This 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
    DCurro almost 10 years
    This is a great solution. I have the most maintainable C++ resource manager that I've ever dealt with.
  • FractalSpace
    FractalSpace over 9 years
    Awesome! @RonnyBrendel, a small tweak can make it a single-file solution (see my answer below)
  • Vincent
    Vincent over 9 years
    You and user3360260 have a good solution. Why not having a multimap instead?
  • yamifm0f
    yamifm0f over 9 years
    I 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 method static const char* getNameByEnum(MetaSyntacticVariable e) { /*code to return the static string*/ }
  • chappjc
    chappjc over 8 years
    Good idea using a strongly typed enum (enum class). Here's a demo: cpp.sh/4ife
  • chappjc
    chappjc over 8 years
    The last caveat (won't work for a variable) is a big drawback, but +1 anyway.
  • Jason Harrison
    Jason Harrison over 8 years
    Does this work with externally defined enumerations/symbols. For example, OS defined or library defined symbols with gaps in the numbering?
  • AlwaysLearning
    AlwaysLearning over 8 years
    Very nice, but does not compile if put inside a class (I could not figure out why).
  • srcspider
    srcspider almost 8 years
    Actually 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
    463035818_is_not_a_number almost 7 years
    seems 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
    Craig.Feied over 6 years
    I 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
    kyb over 6 years
    Works only if you won't set special numeric values to enum entries.
  • Th. Thielemann
    Th. Thielemann over 6 years
    Here is an online generator: th-thielemann.de/tools/cpp-enum-to-string.html
  • Francois Bertrand
    Francois Bertrand over 5 years
    Fantastic 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
    Philippe Carphin almost 4 years
    I think this could also be adapted with #define X(a) case a: return #a; and char *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.
    Sz. over 2 years
    Nice, but it kinda seems practical for diagnostics only. BTW, apropos principles (like "DRY"): cout << Animal::CAT should still just print 1 (rather than Animal::CAT), as per the principle of least surprise. :)
  • Milan
    Milan over 2 years
    Can 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}; outside main 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 replaced SOME_ENUMs with MyClr and Foo, Bar, Baz with R, G, B respectively. But, in main, 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
    Andrew about 2 years
    You have to declare each member? What a complete waste!