C++:将枚举值打印为文本

tib*_*boo 76 c++ enums

如果我有这样的枚举

enum Errors
{ErrorA=0, ErrorB, ErrorC};
Run Code Online (Sandbox Code Playgroud)

然后我想打印到控制台

Errors anError = ErrorA;
cout<<anError;/// 0 will be printed
Run Code Online (Sandbox Code Playgroud)

但我想要的是文本"ErrorA",我可以不使用if/switch吗?
你有什么解决方案?

Sig*_*erm 56

使用地图:

#include <iostream>
#include <map>
#include <string>

enum Errors {ErrorA=0, ErrorB, ErrorC};

std::ostream& operator<<(std::ostream& out, const Errors value){
    static std::map<Errors, std::string> strings;
    if (strings.size() == 0){
#define INSERT_ELEMENT(p) strings[p] = #p
        INSERT_ELEMENT(ErrorA);     
        INSERT_ELEMENT(ErrorB);     
        INSERT_ELEMENT(ErrorC);             
#undef INSERT_ELEMENT
    }   

    return out << strings[value];
}

int main(int argc, char** argv){
    std::cout << ErrorA << std::endl << ErrorB << std::endl << ErrorC << std::endl;
    return 0;   
}
Run Code Online (Sandbox Code Playgroud)

使用线性搜索结构数组:

#include <iostream>
#include <string>

enum Errors {ErrorA=0, ErrorB, ErrorC};

std::ostream& operator<<(std::ostream& out, const Errors value){
#define MAPENTRY(p) {p, #p}
    const struct MapEntry{
        Errors value;
        const char* str;
    } entries[] = {
        MAPENTRY(ErrorA),
        MAPENTRY(ErrorB),
        MAPENTRY(ErrorC),
        {ErrorA, 0}//doesn't matter what is used instead of ErrorA here...
    };
#undef MAPENTRY
    const char* s = 0;
    for (const MapEntry* i = entries; i->str; i++){
        if (i->value == value){
            s = i->str;
            break;
        }
    }

    return out << s;
}

int main(int argc, char** argv){
    std::cout << ErrorA << std::endl << ErrorB << std::endl << ErrorC << std::endl;
    return 0;   
}
Run Code Online (Sandbox Code Playgroud)

使用开关/外壳:

#include <iostream>
#include <string>

enum Errors {ErrorA=0, ErrorB, ErrorC};

std::ostream& operator<<(std::ostream& out, const Errors value){
    const char* s = 0;
#define PROCESS_VAL(p) case(p): s = #p; break;
    switch(value){
        PROCESS_VAL(ErrorA);     
        PROCESS_VAL(ErrorB);     
        PROCESS_VAL(ErrorC);
    }
#undef PROCESS_VAL

    return out << s;
}

int main(int argc, char** argv){
    std::cout << ErrorA << std::endl << ErrorB << std::endl << ErrorC << std::endl;
    return 0;   
}
Run Code Online (Sandbox Code Playgroud)

  • -1.只需做一个switch-case而不是使用hash-map.增加复杂性并不是一件好事. (12认同)
  • 好点子.下次我会:)但现在我看到你已经编辑了你的帖子来添加我正在寻找的那种功能.做得好! (8认同)
  • `#p`是字符串化p的预处理器.所以调用`PROCESS_VAL(ErrorA)`将输出:`case(ErrorA):s ="ErrorA"; 打破;`. (2认同)
  • 最后一个开关/案例非常棒! (2认同)

Igo*_*Oks 25

使用具有匹配值的字符串数组或向量:

char *ErrorTypes[] =
{
    "errorA",
    "errorB",
    "errorC"
};

cout << ErrorTypes[anError];
Run Code Online (Sandbox Code Playgroud)

编辑:上面的解决方案适用于枚举是连续的,即从0开始并且没有指定的值.它将与问题中的枚举完美配合.

要进一步证明enum不是从0开始的情况,请使用:

cout << ErrorTypes[anError - ErrorA];
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,enum允许我们为元素赋值.如果您有非contiguos枚举,行'枚举状态{OK = 0,失败= -1,OutOfMemory = -2,IOError = -1000,ConversionError = -2000}`(以便以后可以添加IOErrors),如何处理工作至-1001-1999范围) (4认同)
  • 那么,如果我的同事将NewValue添加到枚举并且不更新ErrorTypes数组,那么ErrorTypes [NewValue]会产生什么?我如何处理负枚举值? (2认同)
  • @Luther:您必须更新ErrorTypes.同样,在简单性和普遍性之间存在权衡,取决于对用户更重要的事情.负枚举值有什么问题? (2认同)

Phi*_*ipp 14

这是一个基于Boost.Preprocessor的示例:

#include <iostream>

#include <boost/preprocessor/punctuation/comma.hpp>
#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/comparison/equal.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/seq/seq.hpp>


#define DEFINE_ENUM(name, values)                               \
  enum name {                                                   \
    BOOST_PP_SEQ_FOR_EACH(DEFINE_ENUM_VALUE, , values)          \
  };                                                            \
  inline const char* format_##name(name val) {                  \
    switch (val) {                                              \
      BOOST_PP_SEQ_FOR_EACH(DEFINE_ENUM_FORMAT, , values)       \
    default:                                                    \
        return 0;                                               \
    }                                                           \
  }

#define DEFINE_ENUM_VALUE(r, data, elem)                        \
  BOOST_PP_SEQ_HEAD(elem)                                       \
  BOOST_PP_IIF(BOOST_PP_EQUAL(BOOST_PP_SEQ_SIZE(elem), 2),      \
               = BOOST_PP_SEQ_TAIL(elem), )                     \
  BOOST_PP_COMMA()

#define DEFINE_ENUM_FORMAT(r, data, elem)             \
  case BOOST_PP_SEQ_HEAD(elem):                       \
  return BOOST_PP_STRINGIZE(BOOST_PP_SEQ_HEAD(elem));


DEFINE_ENUM(Errors,
            ((ErrorA)(0))
            ((ErrorB))
            ((ErrorC)))

int main() {
  std::cout << format_Errors(ErrorB) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

  • @Fabio Fracassi:"这个解决方案不依赖于外部工具"Boost是一个外部工具 - 非标准C++库.此外,它有点太长了.问题的解决方案应该尽可能简单.这个没有资格...... (3认同)
  • +1,这个解决方案不依赖于外部工具,如上面的lua答案,但是纯C++,它遵循DRY原则,用户语法是可读的(如果格式正确.顺便说一下,你不需要反斜杠当使用看起来更自然的DEFINE_ENUM时,IMO) (2认同)
  • 实际上,您可以将大部分代码(实际上除了实际定义之外的所有代码)放入单个标头中.所以这实际上是这里提出的最短的解决方案.并且对于外部的提升,是的,但不如语言脚本用于预处理源的部分,就像上面的lua脚本一样.除了boost是如此接近标准,它应该在每个C++程序员工具箱中.当然是恕我直言 (2认同)
  • 非常好,我认为唯一的改进是使用“enum class”而不是普通的枚举,这需要在 switch 生成中传递“name”(作为“data”)并在每行前面添加“data::”在“DEFINE_ENUM_FORMAT”中生成。除此之外,这是一个很棒的解决方案,谢谢!编辑:另外,我添加了一个 ostream 运算符以将其与流一起使用,而不是调用格式函数。 (2认同)

Nor*_*ame 6

这里有一个讨论可能会有所帮助:Is there a simple way to conversion C++ enum to string?

更新: Here#sa 是 Lua 脚本,它为遇到的每个命名枚举创建一个运算符<<。这可能需要一些工作才能使其适用于不太简单的情况 [1]:

function make_enum_printers(s)
    for n,body in string.gmatch(s,'enum%s+([%w_]+)%s*(%b{})') do
    print('ostream& operator<<(ostream &o,'..n..' n) { switch(n){') 
    for k in string.gmatch(body,"([%w_]+)[^,]*") do
    print('  case '..k..': return o<<"'..k..'";')
    end
    print('  default: return o<<"(invalid value)"; }}')
    end
end

local f=io.open(arg[1],"r")
local s=f:read('*a')
make_enum_printers(s)
Run Code Online (Sandbox Code Playgroud)

鉴于此输入:

enum Errors
{ErrorA=0, ErrorB, ErrorC};

enum Sec {
    X=1,Y=X,foo_bar=X+1,Z
};
Run Code Online (Sandbox Code Playgroud)

它生产:

ostream& operator<<(ostream &o,Errors n) { switch(n){
  case ErrorA: return o<<"ErrorA";
  case ErrorB: return o<<"ErrorB";
  case ErrorC: return o<<"ErrorC";
  default: return o<<"(invalid value)"; }}
ostream& operator<<(ostream &o,Sec n) { switch(n){
  case X: return o<<"X";
  case Y: return o<<"Y";
  case foo_bar: return o<<"foo_bar";
  case Z: return o<<"Z";
  default: return o<<"(invalid value)"; }}
Run Code Online (Sandbox Code Playgroud)

所以这可能是你的一个开始。

[1] 不同或非命名空间范围中的枚举、带有包含 komma 的初始化表达式的枚举等。

  • Boost PP *也是*一个维护问题,因为你需要每个人都讲 Boost PP 元语言,这是*可怕的*,容易损坏(通常会给出不可用的错误消息)并且可用性有限(lua/python/perl 可以从任意外部数据生成代码)。它增加了您的依赖项列表,由于项目政策甚至可能不允许这样做。此外,它是侵入性的,因为它要求您在 DSL 中定义枚举。您最喜欢的源代码工具或 IDE 可能会遇到麻烦。最后但并非最不重要的一点是:您无法在扩展中设置断点。 (4认同)
  • 我认为下面的 Boost PP 解决方案(来自 Philip)更好,因为使用外部工具的维护成本非常昂贵。但没有-1,因为答案在其他方面是有效的 (2认同)

Mar*_*ram 6

每当定义枚举时,我都会使用字符串数组:

配置文件.h

#pragma once

struct Profile
{
    enum Value
    {
        Profile1,
        Profile2,
    };

    struct StringValueImplementation
    {
        const wchar_t* operator[](const Profile::Value profile)
        {
            switch (profile)
            {
            case Profile::Profile1: return L"Profile1";
            case Profile::Profile2: return L"Profile2";
            default: ASSERT(false); return NULL;
            }
        }
    };

    static StringValueImplementation StringValue;
};
Run Code Online (Sandbox Code Playgroud)

配置文件.cpp

#include "Profile.h"

Profile::StringValueImplementation Profile::StringValue;
Run Code Online (Sandbox Code Playgroud)


jxh*_*jxh 6

如果您愿意enum在外部文件中列出您的条目,您可以使用更简单的预处理器技巧。

/* file: errors.def */
/* syntax: ERROR_DEF(name, value) */
ERROR_DEF(ErrorA, 0x1)
ERROR_DEF(ErrorB, 0x2)
ERROR_DEF(ErrorC, 0x4)
Run Code Online (Sandbox Code Playgroud)

然后在源文件中,您将该文件视为包含文件,但您可以定义要ERROR_DEF执行的操作。

enum Errors {
#define ERROR_DEF(x,y) x = y,
#include "errors.def"
#undef ERROR_DEF
};

static inline std::ostream & operator << (std::ostream &o, Errors e) {
    switch (e) {
    #define ERROR_DEF(x,y) case y: return o << #x"[" << y << "]";
    #include "errors.def"
    #undef ERROR_DEF
    default: return o << "unknown[" << e << "]";
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您使用某些源浏览工具(如 cscope),则必须让它知道外部文件。


Xia*_*iao 6

此解决方案不需要您使用任何数据结构或创建不同的文件。

基本上,您在 #define 中定义所有枚举值,然后在运算符 << 中使用它们。与@jxh的答案非常相似。

最终迭代的 ideone 链接:http://ideone.com/hQTKQp

完整代码:

#include <iostream>

#define ERROR_VALUES ERROR_VALUE(NO_ERROR)\
ERROR_VALUE(FILE_NOT_FOUND)\
ERROR_VALUE(LABEL_UNINITIALISED)

enum class Error
{
#define ERROR_VALUE(NAME) NAME,
    ERROR_VALUES
#undef ERROR_VALUE
};

inline std::ostream& operator<<(std::ostream& os, Error err)
{
    int errVal = static_cast<int>(err);
    switch (err)
    {
#define ERROR_VALUE(NAME) case Error::NAME: return os << "[" << errVal << "]" #NAME;
    ERROR_VALUES
#undef ERROR_VALUE
    default:
        // If the error value isn't found (shouldn't happen)
        return os << errVal;
    }
}

int main() {
    std::cout << "Error: " << Error::NO_ERROR << std::endl;
    std::cout << "Error: " << Error::FILE_NOT_FOUND << std::endl;
    std::cout << "Error: " << Error::LABEL_UNINITIALISED << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

Error: [0]NO_ERROR
Error: [1]FILE_NOT_FOUND
Error: [2]LABEL_UNINITIALISED
Run Code Online (Sandbox Code Playgroud)

这样做的一个好处是,如果您认为需要,您还可以为每个错误指定自己的自定义消息:

#include <iostream>

#define ERROR_VALUES ERROR_VALUE(NO_ERROR, "Everything is fine")\
ERROR_VALUE(FILE_NOT_FOUND, "File is not found")\
ERROR_VALUE(LABEL_UNINITIALISED, "A component tried to the label before it was initialised")

enum class Error
{
#define ERROR_VALUE(NAME,DESCR) NAME,
    ERROR_VALUES
#undef ERROR_VALUE
};

inline std::ostream& operator<<(std::ostream& os, Error err)
{
    int errVal = static_cast<int>(err);
    switch (err)
    {
#define ERROR_VALUE(NAME,DESCR) case Error::NAME: return os << "[" << errVal << "]" #NAME <<"; " << DESCR;
    ERROR_VALUES
#undef ERROR_VALUE
    default:
        return os << errVal;
    }
}

int main() {
    std::cout << "Error: " << Error::NO_ERROR << std::endl;
    std::cout << "Error: " << Error::FILE_NOT_FOUND << std::endl;
    std::cout << "Error: " << Error::LABEL_UNINITIALISED << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

Error: [0]NO_ERROR; Everything is fine
Error: [1]FILE_NOT_FOUND; File is not found
Error: [2]LABEL_UNINITIALISED; A component tried to the label before it was initialised
Run Code Online (Sandbox Code Playgroud)

如果您喜欢使错误代码/描述非常具有描述性,您可能不希望它们出现在生产版本中。关闭它们以便仅打印值很容易:

inline std::ostream& operator<<(std::ostream& os, Error err)
{
    int errVal = static_cast<int>(err);
    switch (err)
    {
    #ifndef PRODUCTION_BUILD // Don't print out names in production builds
    #define ERROR_VALUE(NAME,DESCR) case Error::NAME: return os << "[" << errVal << "]" #NAME <<"; " << DESCR;
        ERROR_VALUES
    #undef ERROR_VALUE
    #endif
    default:
        return os << errVal;
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

Error: 0
Error: 1
Error: 2
Run Code Online (Sandbox Code Playgroud)

如果是这种情况,找到错误号 525 将是一个 PITA。我们可以像这样手动指定初始枚举中的数字:

#define ERROR_VALUES ERROR_VALUE(NO_ERROR, 0, "Everything is fine")\
ERROR_VALUE(FILE_NOT_FOUND, 1, "File is not found")\
ERROR_VALUE(LABEL_UNINITIALISED, 2, "A component tried to the label before it was initialised")\
ERROR_VALUE(UKNOWN_ERROR, -1, "Uh oh")

enum class Error
{
#define ERROR_VALUE(NAME,VALUE,DESCR) NAME=VALUE,
    ERROR_VALUES
#undef ERROR_VALUE
};

inline std::ostream& operator<<(std::ostream& os, Error err)
{
    int errVal = static_cast<int>(err);
    switch (err)
    {
#ifndef PRODUCTION_BUILD // Don't print out names in production builds
#define ERROR_VALUE(NAME,VALUE,DESCR) case Error::NAME: return os << "[" #VALUE  "]" #NAME <<"; " << DESCR;
    ERROR_VALUES
#undef ERROR_VALUE
#endif
    default:
        return os <<errVal;
    }
}
    ERROR_VALUES
#undef ERROR_VALUE
#endif
    default:
    {
        // If the error value isn't found (shouldn't happen)
        return os << static_cast<int>(err);
        break;
    }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

Error: [0]NO_ERROR; Everything is fine
Error: [1]FILE_NOT_FOUND; File is not found
Error: [2]LABEL_UNINITIALISED; A component tried to the label before it was initialised
Error: [-1]UKNOWN_ERROR; Uh oh
Run Code Online (Sandbox Code Playgroud)


MrP*_*es7 5

这是个好办法,

enum Rank { ACE = 1, DEUCE, TREY, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING };
Run Code Online (Sandbox Code Playgroud)

用字符数组数组打印它

const char* rank_txt[] = {"Ace", "Deuce", "Trey", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Four", "King" } ;
Run Code Online (Sandbox Code Playgroud)

像这样

std::cout << rank_txt[m_rank - 1]
Run Code Online (Sandbox Code Playgroud)

  • 如果我的枚举从 2000 开始怎么办?此解决方案将不起作用。 (2认同)