Jump to content
  • Advertisement
Sign in to follow this  
BS-er

C++ enum names as strings

This topic is 4092 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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:


apples
oranges
pears


and generated a .h file like:


#ifndef fruits_H
#define fruits_H

enum fruits { apples,oranges,pears };
const char *fruits_str[]={ "apples","oranges","pears" };

#endif


Obviously this was a pretty trivial program to implement.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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)

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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:


apples
oranges
pears


and generated a .h file like:


#ifndef fruits_H
#define fruits_H

enum 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)

Share this post


Link to post
Share on other sites
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



Share this post


Link to post
Share on other sites
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"
};

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!