How to convert an enum type variable to a string?
This is the pre processor block
#ifndef GENERATE_ENUM_STRINGS
#define DECL_ENUM_ELEMENT( element ) element
#define BEGIN_ENUM( ENUM_NAME ) typedef enum tag##ENUM_NAME
#define END_ENUM( ENUM_NAME ) ENUM_NAME; \
char* getString##ENUM_NAME(enum tag##ENUM_NAME index);
#else
#define DECL_ENUM_ELEMENT( element ) #element
#define BEGIN_ENUM( ENUM_NAME ) char* gs_##ENUM_NAME [] =
#define END_ENUM( ENUM_NAME ) ; char* getString##ENUM_NAME(enum \
tag##ENUM_NAME index){ return gs_##ENUM_NAME [index]; }
#endif
Enum definition
BEGIN_ENUM(OsType)
{
DECL_ENUM_ELEMENT(WINBLOWS),
DECL_ENUM_ELEMENT(HACKINTOSH),
} END_ENUM(OsType)
Call using
getStringOsType(WINBLOWS);
Taken from here. How cool is that ? :)
There really is no beautiful way of doing this. Just set up an array of strings indexed by the enum.
If you do a lot of output, you can define an operator<< that takes an enum parameter and does the lookup for you.
The naive solution, of course, is to write a function for each enumeration that performs the conversion to string:
enum OS_type { Linux, Apple, Windows };
inline const char* ToString(OS_type v)
{
switch (v)
{
case Linux: return "Linux";
case Apple: return "Apple";
case Windows: return "Windows";
default: return "[Unknown OS_type]";
}
}
This, however, is a maintenance disaster. With the help of the Boost.Preprocessor library, which can be used with both C and C++ code, you can easily take advantage of the preprocessor and let it generate this function for you. The generation macro is as follows:
#include <boost/preprocessor.hpp>
#define X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE(r, data, elem) \
case elem : return BOOST_PP_STRINGIZE(elem);
#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators) \
enum name { \
BOOST_PP_SEQ_ENUM(enumerators) \
}; \
\
inline const char* ToString(name v) \
{ \
switch (v) \
{ \
BOOST_PP_SEQ_FOR_EACH( \
X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE, \
name, \
enumerators \
) \
default: return "[Unknown " BOOST_PP_STRINGIZE(name) "]"; \
} \
}
The first macro (beginning with X_
) is used internally by the second. The second macro first generates the enumeration, then generates a ToString
function that takes an object of that type and returns the enumerator name as a string (this implementation, for obvious reasons, requires that the enumerators map to unique values).
In C++ you could implement the ToString
function as an operator<<
overload instead, but I think it's a bit cleaner to require an explicit "ToString
" to convert the value to string form.
As a usage example, your OS_type
enumeration would be defined as follows:
DEFINE_ENUM_WITH_STRING_CONVERSIONS(OS_type, (Linux)(Apple)(Windows))
While the macro looks at first like it is a lot of work, and the definition of OS_type
looks rather foreign, remember that you have to write the macro once, then you can use it for every enumeration. You can add additional functionality to it (e.g., a string-form to enum conversion) without too much trouble, and it completely solves the maintenance problem, since you only have to provide the names once, when you invoke the macro.
The enumeration can then be used as if it were defined normally:
#include <iostream>
int main()
{
OS_type t = Windows;
std::cout << ToString(t) << " " << ToString(Apple) << std::endl;
}
The code snippets in this post, beginning with the #include <boost/preprocessor.hpp>
line, can be compiled as posted to demonstrate the solution.
This particular solution is for C++ as it uses C++-specific syntax (e.g., no typedef enum
) and function overloading, but it would be straightforward to make this work with C as well.