C++ enum names as strings
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.
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:
and generated a .h file like:
Obviously this was a pretty trivial program to implement.
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<<
I suppose you could write a program to generate this as well.
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.
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.:
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)
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.
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
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" };
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement