How to convert an enum type variable to a string?

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.

Leave a Comment