views:

164

answers:

3

I would like to be able to write:

cout << enumalpha << Monday;

and get printed on console:

Monday

P.S. Monday is an enum type.

+2  A: 

(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
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
GMan
+4  A: 

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.

GMan
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
Ouch, if you're going to use the preprocessor at least make it easy on the caller :p
Matthieu M.
@James: Sunday...hm. Damn unicorns eating code again. <:(
GMan
+7  A: 

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.
Wow, very nice. +1
GMan