C++ enum names as strings

Started by
22 comments, last by sirGustav 17 years, 1 month ago
In Ada and probably numerous other languages, string representations of each enum value name are available in an elegant manner, and the language has built-in functions for converting between the actual value and the string representation, and vice versa. The language implementation is able to do this in an optimized manner as well. I suspect that it can't be as easy as that in C++, given its lower-level nature. If that's the case it's unfortunate, since it seems to be a recurring pattern for me to need to convert between enum values and string representations of those values. I was wondering if there is anything out there maybe in the form of a template that makes it easier for us to convert enum values to string representations and vice versa. Hopefully something compact as well. I thought I spotted something similar in some proposed Boost features, but there wasn't enough information on it, and I had hoped to avoid the headaches and confusion of installing a large template library for a few features. Thanks for any advice.
Value of good ideas: 10 cents per dozen.Implementation of the good ideas: Priceless.Machines, Anarchy and Destruction - A 3D action sim with a hint of strategy
Advertisement
You are correct that you can't do this in a built in way with C++.

When I needed something like this a couple of years ago, I wrote a little program that took a text file as input, for example say we had fruits.txt:

applesorangespears


and generated a .h file like:

#ifndef fruits_H#define fruits_Henum fruits { apples,oranges,pears };const char *fruits_str[]={ "apples","oranges","pears" };#endif


Obviously this was a pretty trivial program to implement.
I usually keep synchronized my enum and string arrays manually, with a static assert to help me catch at compile time to ensure they are the same size, and help to prevent forgetting to update one after the other.
Another approach is to overload operator<<

enum fruits{   apples,   oranges,   pears};std::ostream& operator<<( std::ostream& os, const fruits& fruit ){   switch( fruit )   {      case apples: os << "apples"; break;      case oranges: os << "oranges"; break;      case pears: os << "pears"; break;   }}


I suppose you could write a program to generate this as well.
This is rather clever, even though a bit ugly.

Alternatives exist at the IDE-level. Using vim, Emacs, or Visual Studio you can use a script/macro that generates a string table from an enumeration automatically. Vim already has such a script here. It should be trivial to write one for Visual Studio using C#/VB.NET.

Although I'm sure there are more sophisticated approaches one could take, I generally use a std::map, e.g.:

std::map<Uint16,std::string> formats = boost::assign::map_list_of    (AUDIO_U8,     "AUDIO_U8"    )    (AUDIO_S8,     "AUDIO_S8"    )    (AUDIO_U16LSB, "AUDIO_U16LSB")    (AUDIO_S16LSB, "AUDIO_S16LSB")    (AUDIO_U16MSB, "AUDIO_U16MSB")    (AUDIO_S16MSB, "AUDIO_S16MSB")    (AUDIO_U16,    "AUDIO_U16"   )    (AUDIO_S16,    "AUDIO_S16"   )    (AUDIO_U16SYS, "AUDIO_U16SYS")    (AUDIO_S16SYS, "AUDIO_S16SYS");

Advantages of this approach include:

1. Easier to maintain than a switch statement
2. More flexible than overloading operator<<()
3. Enumerants can have arbitrary values (as opposed to the array method)
4. 'Invalid' enumerants won't lead to UB (as opposed to the array method)
I've used EasilyConfused's pattern in the past (though often with a "MAX_FRUIT" enum at the end which helps in looping over the group and similar constraint needs).

jyk's suggestion though is clearly superior in the face of enums which aren't simply 0->MAX.
Quote:Original post by EasilyConfused
You are correct that you can't do this in a built in way with C++.

When I needed something like this a couple of years ago, I wrote a little program that took a text file as input, for example say we had fruits.txt:

applesorangespears


and generated a .h file like:

#ifndef fruits_H#define fruits_Henum fruits { apples,oranges,pears };const char *fruits_str[]={ "apples","oranges","pears" };#endif


Obviously this was a pretty trivial program to implement.


Here's a way to avoid putting more tools into your build chain (but it uses boost, and worse than templates --- it uses macros):

#include <boost/preprocessor.hpp>#define SEQ (apples)(oranges)(pears)#define TO_STR(unused,data,elem) BOOST_PP_STRINGIZE(elem) ,enum fruits { BOOST_PP_SEQ_ENUM(SEQ) };const char * fruit_strings[]={ BOOST_PP_SEQ_FOR_EACH(TO_STR,~,SEQ) };#undef SEQ#undef CAT


EDIT: Enterprisey-fied:

#include <boost/preprocessor.hpp>#define PROJECT_PREFIX_DO_EVIL_TO_STR(unused,data,elem) BOOST_PP_STRINGIZE(elem) ,#define PROJECT_PREFIX_DO_EVIL(enum_,strings,elements)     \ enum enum_ { BOOST_PP_SEQ(elements) };                     \ const char * strings[]={ BOOST_PP_SEQ_FOR_EACH(PROJECT_PREFIX_DO_EVIL_TO_STR,~,SEQ) };//-----------------------PROJECT_PREFIX_DO_EVIL(fruit,fruit_strs,(apple)(pear)(pineapple))PROJECT_PREFIX_DO_EVIL(animal,animal_strs,(dog)(cat)(panda)(monkey))


(edit/note: trailing whitespaces to preserve \s inserted)
Quote:Original post by Simian Man
Another approach is to overload operator<<

enum fruits{   apples,   oranges,   pears};std::ostream& operator<<( std::ostream& os, const fruits& fruit ){   switch( fruit )   {      case apples: os << "apples"; break;      case oranges: os << "oranges"; break;      case pears: os << "pears"; break;   }}


I suppose you could write a program to generate this as well.



Try that and include this header from several different cpp's... Now
take a hex-editor and search the generated exe for "apples" - the
entire lookup table will be there as often as you included it.

Regards



visit my website at www.kalmiya.com
Quote:Original post by Muhammad Haggag
This is rather clever, even though a bit ugly.

Here is a simplified version:
    #define DAYS_OF_THE_WEEK         \         ENUM_OR_STRING( Sunday ),    \         ENUM_OR_STRING( Monday ),    \         ENUM_OR_STRING( Tuesday ),   \         ENUM_OR_STRING( Wednesday ), \         ENUM_OR_STRING( Thursday ),  \         ENUM_OR_STRING( Friday ),    \         ENUM_OR_STRING( Saturday )        // Enum        #undef ENUM_OR_STRING    #define ENUM_OR_STRING( x ) x        enum DaysOfTheWeek    {        DAYS_OF_THE_WEEK    };        // Strings        #undef ENUM_OR_STRING    #define ENUM_OR_STRING( x ) #x        char * DaysOfTheWeekStrings[] =    {        DAYS_OF_THE_WEEK    }; 
You could also put the elements in an include file instead of a macro:
---- DaysOfTheWeek.h        ENUM_OR_STRING( Sunday ),    ENUM_OR_STRING( Monday ),    ENUM_OR_STRING( Tuesday ),    ENUM_OR_STRING( Wednesday ),    ENUM_OR_STRING( Thursday ),    ENUM_OR_STRING( Friday ),    ENUM_OR_STRING( Saturday )    ---- source file           // Enum        #undef ENUM_OR_STRING    #define ENUM_OR_STRING( x ) x        enum DaysOfTheWeek    {        #include "DaysOfTheWeek.h"    };        // Strings        #undef ENUM_OR_STRING    #define ENUM_OR_STRING( x ) #x        char * DaysOfTheWeekStrings[] =    {        #include "DaysOfTheWeek.h"    }; 
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!

This topic is closed to new replies.

Advertisement