【问题标题】:Is it possible to define enumalpha?是否可以定义 enumalpha ?
【发布时间】:2010-04-03 16:15:05
【问题描述】:

我希望能够写:

cout << enumalpha << Monday;

并在控制台上打印:

星期一

附: Monday 是枚举类型。

【问题讨论】:

  • 不,一般情况下不会。它必须知道所有可能的枚举才能工作。您当然可以编写一个函数 ToString( enum Days ); 将 Days 枚举值转换为字符串。
  • @Neil 谢谢,我想我会这样做的。
  • @Neil 但是这些枚举名称必须存储在某个地方,因此应该有办法将它们分叉出来。还是不行?
  • 枚举名称在编译时由编译器转换为数值。它们在运行时并不比变量名更容易访问。

标签: c++ enums iostream


【解决方案1】:

好的,让我们开始所有的预处理器吧:)

预期使用方式:

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
}

黑魔法,涉及有用的 Boost.Preprocessor 库。

#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; } }

有更好的方法,我个人使用这个小宏将所有内容存储在排序良好的对向量中,类型为静态,如果心情(或需要),它还允许我遍历枚举的值罢工:)

这很不幸,尽管该语言不支持它。如果有的话,我希望枚举对代码集非常方便......

【讨论】:

    【解决方案2】:

    据我所知,您想要的确切形式是不可能的。正如尼尔所说,名字对我们来说只是人类;编译器处理这些值。

    也就是说,您可以创建一个实用程序来为枚举命名。这是一个例子:

    #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; \
                }
    

    你会这样使用它:

    #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;
    }
    

    尝试使用“未命名”类型返回其数值。

    注意,这里实际上并没有强制执行枚举;例如,您可以使用它来命名整数值。如果你这样做了,operator&lt;&lt; 将是模棱两可的。

    如果您可以使用 Boost,请使用它们的类型特征 is_enum(相当复杂)并静态断言它就是这种情况。为此,更改将是:

    #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) \
                    {
    

    现在,如果类型不是枚举,则编译错误至少指向您尝试定义枚举名称的行。

    【讨论】:

    • 我想到了这个,但后来我决定在星期六早上写宏为时过早^H^H^H^H^H^H^H下午。 ;-) 很不错。不过,星期天怎么了?这也是有趣的一天!
    • 哎呀,如果你要使用预处理器,至少让调用者更容易:p
    【解决方案3】:

    (不,这不能回答一般情况下的问题,但它可能仍然有人感兴趣)

    正如 Neil 在 cmets 中对这个问题所说的那样,在枚举的一般情况下,您不能这样做。但是,对于个别情况,您可以为枚举类型重载流插入运算符 (&lt;&lt;):

    #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;
    }
    

    比如上面的代码,这样:

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

    将打印:

    Saturday
    

    【讨论】:

    • 该死,如果我等了一分钟,我就会被提醒我应该构建流操作符。 :|我马上偷这个。 :) +1
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-02
    • 1970-01-01
    • 1970-01-01
    • 2011-06-08
    • 2013-05-11
    • 1970-01-01
    相关资源
    最近更新 更多