1

I would like to be able to write:

cout << enumalpha << Monday;

and get printed on console:

Monday

P.S. Monday is an enum type.

zmbush
  • 2,790
  • 1
  • 17
  • 35
There is nothing we can do
  • 23,727
  • 30
  • 106
  • 194
  • No, not in the general case. It would have to know about all possible enumerations in order for it to work. You can of course write a function `ToString( enum Days );` which converts a Days enum value to a string. –  Apr 03 '10 at 16:18
  • @Neil Thanks I think I'll do that. – There is nothing we can do Apr 03 '10 at 16:28
  • @Neil But those enums names must be stored somewhere so there should be a way to fork them out. Or not? – There is nothing we can do Apr 03 '10 at 16:38
  • 2
    enum names are converted to numeric values by the compiler at compile time. They are no more accessible at run-time than are the names of variables. –  Apr 03 '10 at 16:39

3 Answers3

8

Okay, let's go all preprocessor then :)

Intended way of use:

DEFINE_ENUM(DayOfWeek, (Monday)(Tuesday)(Wednesday)
                       (Thursday)(Friday)(Saturday)(Sunday))

int main(int argc, char* argv[])
{
  DayOfWeek_t i = DayOfWeek::Monday;
  std::cout << i << std::endl;            // prints Monday

  std::cin >> i >> std::endl;             // reads the content of a string and
                                          // deduce the corresponding enum value
}

Dark magic, involving the helpful Boost.Preprocessor library.

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each.hpp>

#define DEFINE_ENUM_VAL_TO_STR(r, data, elem)                      \
   case BOOST_PP_CAT(data, BOOST_PP_CAT(::, elem)):                \
    return out << BOOST_PP_STRINGIZE(elem);

#define DEFINE_ENUM_STR_TO_VAL(r, data, elem)                      \
   if (s == BOOST_PP_STRINGIZE(elem))                              \
   { i = BOOST_PP_CAT(data, BOOST_PP_CAT(::, elem)) ; } else


#define DEFINE_ENUM(Name, Values)                                  \
   struct Name {                                                   \
     enum type {                                                   \
       Invalid = 0,                                                \
       BOOST_PP_SEQ_ENUM(Values)                                   \
     };                                                            \
   };                                                              \
   typedef Name::type Name##_t;                                    \
   std::ostream& operator<<(std::ostream& out, Name##_t i) {       \
    switch(i) {                                                    \
      BOOST_PP_SEQ_FOR_EACH(DEFINE_ENUM_VAL_TO_STR, Name, Values)  \
    default: return out << "~"; } }                                \
   std::istream& operator>>(std::istream& in, Name##_t& i) {       \
     std::string s; in >> s;                                       \
     BOOST_PP_SEQ_FOR_EACH(DEFINE_ENUM_STR_TO_VAL, Name, Values)   \
     { i = Name##::Invalid; } }

There are better ways, personally I use this little macro to store that all in a nicely sorted vector of pairs, static for the type, it also allows me to iterate through the values of the enum if the mood (or need) strikes :)

It's quite unfortunate though there is no support in the language for that. I would prefer if there was, enum are quite handy for codesets...

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
4

The exact form of what you want is impossible, as far as I know. As Neil said, names are for us mere humans; the compiler deals with the values.

That said, you can make a utility for giving names to enums. Here's an example:

#define ENUM_NAMES_BEGIN(pType) \
    std::ostream& operator<<(std::ostream& pStream, pType pValue) \
            { \
                switch (pValue) \
                {

#define ENUM_NAMES_CASE_NAMED(pValue, pName) \
                case (pValue): \
                    pStream << (pName); \
                    break;

#define ENUM_NAMES_CASE(pValue) ENUM_NAMES_CASE_NAMED(pValue, #pValue)

#define ENUM_NAMES_END(pDefault) \
                default: \
                    pStream << (pDefault); \
                } \
                \
                return pStream; \
            }

You'd use it as such:

#include <iostream>

enum Days
{
    Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
};

enum NotNamed
{
    DontTry, ImNotnamed
};

ENUM_NAMES_BEGIN(Days)
    ENUM_NAMES_CASE(Sunday)
    ENUM_NAMES_CASE(Monday)
    ENUM_NAMES_CASE(Tuesday)
    ENUM_NAMES_CASE(Wednesday)
    ENUM_NAMES_CASE(Thursday)
    ENUM_NAMES_CASE(Friday)
    ENUM_NAMES_CASE_NAMED(Saturday, "Saturday: Fun day!")
ENUM_NAMES_END("")

int main()
{
    Days d = Saturday; // or whatever
    NotNamed n = ImNotnamed;

    std::cout << "Day: " << d << std::endl;
    std::cout << "Not Named: " << n << std::endl;
}

Trying it with a type that is "unnamed" returns its numeric value.

Note, there isn't actually any enforcement of an enum here; you could use this to name integer values, for example. If you did, operator<< would be ambiguous.

If you can use Boost, use their type trait is_enum (which is fairly complex) and static assert that it be the case. For that the change would be:

#include <boost/static_assert.hpp>
#include <boost/type_traits/is_enum.hpp>

#define ENUM_NAMES_BEGIN(pType) \
    std::ostream& operator<<(std::ostream& pStream, pType pValue) \
            { \
                BOOST_STATIC_ASSERT(boost::is_enum<pType>::value); \
                switch (pValue) \
                {

Now if the type is not an enum, the compile-error at least point to the line where you're trying to define the enum names.

GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • I thought of this, but then I decided it was too early on a Saturday morning^H^H^H^H^H^H^H afternoon to write macros. ;-) Very nice. What happened to Sunday, though? It's a fun day too! – James McNellis Apr 03 '10 at 17:19
  • Ouch, if you're going to use the preprocessor at least make it easy on the caller :p – Matthieu M. Apr 03 '10 at 17:20
2

(no, this doesn't answer the question for the general case, but it might still be of interest to somebody)

As Neil says in the comments to this question, you can't do this in the general case for enumerations. However, for individual cases, you can overload the stream insertion operator (<<) for an enumerated type:

#include <iostream>

enum day_of_week
{
    friday,
    saturday,
    sunday
};

std::ostream& operator<<(std::ostream& o, day_of_week day)
{
    switch (day)
    {
    case friday:   o << "Friday";   break;
    case saturday: o << "Saturday"; break;
    case sunday:   o << "Sunday";   break;
    }
    return o;
}

For example, with the above code, this:

std::cout << saturday << std::endl;

will print:

Saturday
James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • Damn, if I had waited one minute I'd of been able to be reminded I should be constructing the stream operator. :| I'm promptly stealing this. :) +1 – GManNickG Apr 03 '10 at 17:06