有没有C ++枚举转换为字符串的简单的方法是什么?

问题描述 投票:112回答:35

假设我们有一些命名的枚举:

enum MyEnum {
      FOO,
      BAR = 0x50
};

我用Google搜索是一个脚本(任何语言),扫描在我的项目的所有头,并产生与每一个枚举功​​能的标题。

char* enum_to_string(MyEnum t);

而像这样的东西一实现:

char* enum_to_string(MyEnum t){
      switch(t){
         case FOO:
            return "FOO";
         case BAR:
            return "BAR";
         default:
            return "INVALID ENUM";
      }
 }

在疑难杂症是真正与通过typedef枚举和无名的空调风格枚举。是否有人知道的东西呢?

编辑:该解决方案不应该修改我的消息来源,除了生成的函数。该枚举是一个API中,所以使用提出到现在的解决方案,只是不是一种选择。

c++ string enums scripting
35个回答
47
投票

你可能想看看GCCXML

运行GCCXML你的示例代码产生:

<GCC_XML>
  <Namespace id="_1" name="::" members="_3 " mangled="_Z2::"/>
  <Namespace id="_2" name="std" context="_1" members="" mangled="_Z3std"/>
  <Enumeration id="_3" name="MyEnum" context="_1" location="f0:1" file="f0" line="1">
    <EnumValue name="FOO" init="0"/>
    <EnumValue name="BAR" init="80"/>
  </Enumeration>
  <File id="f0" name="my_enum.h"/>
</GCC_XML>

你可以使用你喜欢拉出枚举和EnumValue标签并生成所需的代码的任何语言。


5
投票

有趣的路数。这里有一个我以前用的时间长:

在文件myenummap.h:

#include <map>
#include <string>
enum test{ one, two, three, five=5, six, seven };
struct mymap : std::map<unsigned int, std::string>
{
  mymap()
  {
    this->operator[]( one ) = "ONE";
    this->operator[]( two ) = "TWO";
    this->operator[]( three ) = "THREE";
    this->operator[]( five ) = "FIVE";
    this->operator[]( six ) = "SIX";
    this->operator[]( seven ) = "SEVEN";
  };
  ~mymap(){};
};

在main.cpp中

#include "myenummap.h"

...
mymap nummap;
std::cout<< nummap[ one ] << std::endl;

它不是常量,但它的方便。

下面是一个使用C ++ 11种功能的另一种方式。这是常量,不继承STL容器,是一个小整洁:

#include <vector>
#include <string>
#include <algorithm>
#include <iostream>

//These stay together and must be modified together
enum test{ one, two, three, five=5, six, seven };
std::string enum_to_str(test const& e)
{
    typedef std::pair<int,std::string> mapping;
    auto m = [](test const& e,std::string const& s){return mapping(static_cast<int>(e),s);}; 
    std::vector<mapping> const nummap = 
    { 
        m(one,"one"), 
        m(two,"two"), 
        m(three,"three"),
        m(five,"five"),
        m(six,"six"),
        m(seven,"seven"),
    };
    for(auto i  : nummap)
    {
        if(i.first==static_cast<int>(e))
        {
            return i.second;
        }
    }
    return "";
}

int main()
{
//  std::cout<< enum_to_str( 46 ) << std::endl; //compilation will fail
    std::cout<< "Invalid enum to string : [" << enum_to_str( test(46) ) << "]"<<std::endl; //returns an empty string
    std::cout<< "Enumval five to string : ["<< enum_to_str( five ) << "] "<< std::endl; //works
    return 0;
}

4
投票
#include <stdarg.h>
#include <algorithm>
#include <string> 
#include <vector>
#include <sstream>
#include <map>

#define SMART_ENUM(EnumName, ...)                                   \
class EnumName                                                      \
{                                                                   \
private:                                                            \
    static std::map<int, std::string> nameMap;                      \
public:                                                             \
    enum {__VA_ARGS__};                                             \
private:                                                            \
    static std::map<int, std::string> initMap()                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        map<int, string> tmp;                                       \
        for(vector<string>::iterator it = strings.begin();          \
                                               it != strings.end(); \
                                               ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream localStream(*it);                          \
            localStream>> buf_1 >> buf_2;                           \
            if(buf_2.size() > 0)                                    \
                val = atoi(buf_2.c_str());                          \
            tmp[val++] = buf_1;                                     \
        }                                                           \
        return tmp;                                                 \
    }                                                               \
public:                                                             \
    static std::string toString(int aInt)                           \
    {                                                               \
        return nameMap[aInt];                                       \
    }                                                               \
};                                                                  \
std::map<int, std::string>                                          \
EnumName::nameMap = EnumName::initMap();

用法:

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN)
cout<<MyEnum::toString(MyEnum::TWO);
cout<<MyEnum::toString(10);

3
投票

另一个答案:在某些情况下,它是有道理的定义枚举在非编码格式,如CSV,YAML或XML文件,然后生成两个C ++枚举代码和自定义的到字符串代码。这种方法可能会或可能不会在你的应用实践,但它的东西要记住。


3
投票

这是@修改user3360260答案。它具有以下新特点

  • MyEnum fromString(const string&)支持
  • 使用Visual Studio 2012编译
  • 枚举是一个实际的POD类型(不只是const声明),这样你就可以把它分配给一个变量。
  • 附加C ++“范围”功能(在矢量的形式),以允许“的foreach”迭代过枚举

用法:

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN)
MyEnum foo = MyEnum::TWO;
cout << MyEnum::toString(foo);  // static method
cout << foo.toString();         // member method
cout << MyEnum::toString(MyEnum::TWO);
cout << MyEnum::toString(10);
MyEnum foo = myEnum::fromString("TWO");

// C++11 iteration over all values
for( auto x : MyEnum::allValues() )
{
  cout << x.toString() << endl;
}

下面的代码

#define SMART_ENUM(EnumName, ...)                                   \
class EnumName                                                      \
{                                                                   \
public:                                                             \
    EnumName() : value(0) {}                                        \
    EnumName(int x) : value(x) {}                                   \
public:                                                             \
    enum {__VA_ARGS__};                                             \
private:                                                            \
    static void initMap(std::map<int, std::string>& tmp)                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        for(vector<string>::iterator it = strings.begin();          \
                                                it != strings.end(); \
                                                ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream localStream(*it);                          \
            localStream>> buf_1 >> buf_2;                           \
            if(buf_2.size() > 0)                                    \
                val = atoi(buf_2.c_str());                          \
            tmp[val++] = buf_1;                                     \
        }                                                           \
    }                                                               \
    int value;                                                      \
public:                                                             \
    operator int () const { return value; }                         \
    std::string toString(void) const {                              \
            return toString(value);                                 \
    }                                                               \
    static std::string toString(int aInt)                           \
    {                                                               \
        return nameMap()[aInt];                                     \
    }                                                               \
    static EnumName fromString(const std::string& s)                \
    {                                                               \
        auto it = find_if(nameMap().begin(), nameMap().end(), [s](const std::pair<int,std::string>& p) { \
            return p.second == s;                                   \
        });                                                         \
        if (it == nameMap().end()) {                                \
        /*value not found*/                                         \
            throw EnumName::Exception();                            \
        } else {                                                    \
            return EnumName(it->first);                             \
        }                                                           \
    }                                                               \
    class Exception : public std::exception {};                     \
    static std::map<int,std::string>& nameMap() {                   \
      static std::map<int,std::string> nameMap0;                    \
      if (nameMap0.size() ==0) initMap(nameMap0);                   \
      return nameMap0;                                              \
    }                                                               \
    static std::vector<EnumName> allValues() {                      \
      std::vector<EnumName> x{ __VA_ARGS__ };                       \
      return x;                                                     \
    }                                                               \
    bool operator<(const EnumName a) const { return (int)*this < (int)a; } \
};         

请注意,转换的toString是一个快速查找有,而转换fromString是一个缓慢的线性搜索。但字符串是如此昂贵反正(和相关的文件IO),我不觉得需要优化或使用bimap的。


3
投票

qazxsw POI是好的。你不需要有两个不同的宏,通过。 C ++港岛线愉快地包括两次的报头。刚刚离开了包括后卫。

所以你刚才定义foobar.h中

Suma's macro solution

你将包括这样的:

ENUM(Foo, 1)
ENUM(Bar, 2)

enumfactory.h会做2个#define ENUMFACTORY_ARGUMENT "foobar.h" #include "enumfactory.h" s。在第一轮中,它扩展了像苏马的#include ENUMFACTORY_ARGUMENT ENUM;在第二轮ENUM就像DECLARE_ENUM

您可以包括enumfactory.h多次,也只要你在不同#通过定义对ENUMFACTORY_ARGUMENT


2
投票

请注意,您的转换函数最好应返回一个const char *。

如果你能负担得起把你枚举了各自的头文件,你也许可以做这样的事情与宏(哦,这将是丑陋的):

DEFINE_ENUM

其中enum_def.h有:

#include "enum_def.h"
#include "colour.h"
#include "enum_conv.h"
#include "colour.h"

而enum_conv.h有:

#undef ENUM_START
#undef ENUM_ADD
#undef ENUM_END
#define ENUM_START(NAME) enum NAME {
#define ENUM_ADD(NAME, VALUE) NAME = VALUE,
#define ENUM_END };

最后,colour.h有:

#undef ENUM_START
#undef ENUM_ADD
#undef ENUM_END
#define ENUM_START(NAME) const char *##NAME##_to_string(NAME val) { switch (val) {
#define ENUM_ADD(NAME, VALUE) case NAME: return #NAME;
#define ENUM_END default: return "Invalid value"; } }

你可以使用转换函数为:

ENUM_START(colour)
ENUM_ADD(red,   0xff0000)
ENUM_ADD(green, 0x00ff00)
ENUM_ADD(blue,  0x0000ff)
ENUM_END

这是丑陋的,但它是唯一的方法(在预处理级),可以让你定义只是在代码中的一个地方你的枚举。因此,您的代码不容易出错,由于修改的枚举。您的枚举定义和转换功能将始终保持同步。不过,我再说一遍,这是丑陋的:)


2
投票

我这样做有其与宏生成单独的并排的侧枚举包装类。有几个优点:

  • 可以生成它们枚举我不定义(如:操作系统平台头枚举)
  • 可以将范围检查到包装类
  • 可以做“聪明”与位域枚举格式化

不利的一面,当然,是我需要复制的格式化类枚举值,和我没有任何脚本生成它们。除此之外,虽然,它似乎工作得很好。

下面是从我的代码库的枚举,SANS所有的框架代码,实现了宏和模板的例子,但你可以得到的想法:

printf("%s", colour_to_string(colour::red));

然后我们的想法是不是使用EHelpLocation,您使用SEHelpLocation;一切的工作方式相同,但你得到的范围检查和“格式()”在枚举变量本身的方法。如果您需要格式化一个独立的价值,你可以使用CEnumFormatter_EHelpLocation :: FormatEnum(...)。

希望这是有帮助的。我知道这还没有解决有关的脚本实际生成其他类的原来的问题,但我希望的结构有助于有人试图解决同样的问题,或者写这样一个剧本。


2
投票

这里一个文件解决方案(基于优雅的答案被@Marcin:

enum EHelpLocation
{
    HELP_LOCATION_UNKNOWN   = 0, 
    HELP_LOCAL_FILE         = 1, 
    HELP_HTML_ONLINE        = 2, 
};
class CEnumFormatter_EHelpLocation : public CEnumDefaultFormatter< EHelpLocation >
{
public:
    static inline CString FormatEnum( EHelpLocation eValue )
    {
        switch ( eValue )
        {
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCATION_UNKNOWN );
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCAL_FILE );
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_HTML_ONLINE );
        default:
            return FormatAsNumber( eValue );
        }
    }
};
DECLARE_RANGE_CHECK_CLASS( EHelpLocation, CRangeInfoSequential< HELP_HTML_ONLINE > );
typedef ESmartEnum< EHelpLocation, HELP_LOCATION_UNKNOWN, CEnumFormatter_EHelpLocation, CRangeInfo_EHelpLocation > SEHelpLocation;

2
投票

这是我与升压解决方案:

#include <iostream>

#define ENUM_TXT \
X(Red) \
X(Green) \
X(Blue) \
X(Cyan) \
X(Yellow) \
X(Magenta) \

enum Colours {
#   define X(a) a,
ENUM_TXT
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
ENUM_TXT
#   undef X
    0
};

std::ostream& operator<<(std::ostream& os, enum Colours c)
{
    if (c >= ColoursCount || c < 0) return os << "???";
    return os << colours_str[c] << std::endl;
}

int main()
{
    std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl;
}

要创建枚举声明:

#include <boost/preprocessor.hpp>

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

#define X_ENUM_STR_TOENUM_IF(r, data, elem)                                     \
    else if(data == BOOST_PP_STRINGIZE(elem)) return elem;

#define STR_ENUM(name, enumerators)                                             \
    enum name {                                                                 \
        BOOST_PP_SEQ_ENUM(enumerators)                                          \
    };                                                                          \
                                                                                \
    inline const QString enumToStr(name v)                                      \
    {                                                                           \
        switch (v)                                                              \
        {                                                                       \
            BOOST_PP_SEQ_FOR_EACH(                                              \
                X_STR_ENUM_TOSTRING_CASE,                                       \
                name,                                                           \
                enumerators                                                     \
            )                                                                   \
                                                                                \
            default:                                                            \
                return "[Unknown " BOOST_PP_STRINGIZE(name) "]";                \
        }                                                                       \
    }                                                                           \
                                                                                \
    template <typename T>                                                       \
    inline const T strToEnum(QString v);                                        \
                                                                                \
    template <>                                                                 \
    inline const name strToEnum(QString v)                                      \
    {                                                                           \
        if(v=="")                                                               \
            throw std::runtime_error("Empty enum value");                       \
                                                                                \
        BOOST_PP_SEQ_FOR_EACH(                                                  \
            X_ENUM_STR_TOENUM_IF,                                               \
            v,                                                                  \
            enumerators                                                         \
        )                                                                       \
                                                                                \
        else                                                                    \
            throw std::runtime_error(                                           \
                        QString("[Unknown value %1 for enum %2]")               \
                            .arg(v)                                             \
                            .arg(BOOST_PP_STRINGIZE(name))                      \
                                .toStdString().c_str());                        \
    }

对于转换:

STR_ENUM
(
    SERVICE_RELOAD,
        (reload_log)
        (reload_settings)
        (reload_qxml_server)
)

2
投票

即使添加使用更简单,以SERVICE_RELOAD serviceReloadEnum = strToEnum<SERVICE_RELOAD>("reload_log"); QString serviceReloadStr = enumToStr(reload_log);

设置一次:

Jasper Bekkers' fantastic answer

然后,使用:

#define MAKE_ENUM(VAR) VAR,
#define MAKE_STRINGS(VAR) #VAR,
#define MAKE_ENUM_AND_STRINGS(source, enumName, enumStringName) \
    enum enumName { \
    source(MAKE_ENUM) \
    };\
const char* const enumStringName[] = { \
    source(MAKE_STRINGS) \
    };

73
投票

X-宏是最好的解决方案。例:

#include <iostream>

enum Colours {
#   define X(a) a,
#   include "colours.def"
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
#   include "colours.def"
#   undef X
    0
};

std::ostream& operator<<(std::ostream& os, enum Colours c)
{
    if (c >= ColoursCount || c < 0) return os << "???";
    return os << colours_str[c];
}

int main()
{
    std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl;
}

colours.def:

X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)

不过,我通常喜欢下面的方法,以便它可以调整串了一下。

#define X(a, b) a,
#define X(a, b) b,

X(Red, "red")
X(Green, "green")
// etc.

1
投票

与答案0的一个问题是,枚举二进制值不一定从0开始,并不一定是连续的。

当我需要这个,我通常:

  • 拉枚举定义为我的源
  • 编辑得到公正的名字
  • 做一个宏的名称更改为在本案条款,但通常在一行:例中foo:回归“富”;
  • 添加开关,默认及其它语法,使之合法

1
投票

下面的Ruby脚本试图解析头和竣工图旁边的原标题所需的资源。

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)
...
MAKE_ENUM_AND_STRINGS(SOME_ENUM, someEnum, someEnumNames)

使用正则表达式使这个“分析器”相当脆弱,可能无法正常处理您的特定的头文件。

比方说,你有一个头TOTO / A.H,包含枚举MyEnum和MyEnum2定义。该脚本将建立:

#! /usr/bin/env ruby

# Let's "parse" the headers
# Note that using a regular expression is rather fragile
# and may break on some inputs

GLOBS = [
  "toto/*.h",
  "tutu/*.h",
  "tutu/*.hxx"
]

enums = {}
GLOBS.each { |glob|
  Dir[glob].each { |header|
    enums[header] = File.open(header, 'rb') { |f|
      f.read
    }.scan(/enum\s+(\w+)\s+\{\s*([^}]+?)\s*\}/m).collect { |enum_name, enum_key_and_values|
      [
        enum_name, enum_key_and_values.split(/\s*,\s*/).collect { |enum_key_and_value|
          enum_key_and_value.split(/\s*=\s*/).first
        }
      ]
    }
  }
}


# Now we build a .h and .cpp alongside the parsed headers
# using the template engine provided with ruby
require 'erb'

template_h = ERB.new <<-EOS
#ifndef <%= enum_name %>_to_string_h_
#define <%= enum_name %>_to_string_h_ 1

#include "<%= header %>"
char* enum_to_string(<%= enum_name %> e);

#endif
EOS

template_cpp = ERB.new <<-EOS
#include "<%= enum_name %>_to_string.h"

char* enum_to_string(<%= enum_name %> e)
{
  switch (e)
  {<% enum_keys.each do |enum_key| %>
    case <%= enum_key %>: return "<%= enum_key %>";<% end %>
    default: return "INVALID <%= enum_name %> VALUE";
  }
}
EOS

enums.each { |header, enum_name_and_keys|
  enum_name_and_keys.each { |enum_name, enum_keys|
    File.open("#{File.dirname(header)}/#{enum_name}_to_string.h", 'wb') { |built_h|
      built_h.write(template_h.result(binding))
    }

    File.open("#{File.dirname(header)}/#{enum_name}_to_string.cpp", 'wb') { |built_cpp|
      built_cpp.write(template_cpp.result(binding))
    }
  }
}

更强大的解决方案是:

  • 构建所有定义从另一个源枚举和他们的业务来源。这意味着你将在一个XML / YML /任何文件,这是很容易比C / C解析++定义枚举。
  • 通过Avdi的建议使用真正的编译器等。
  • 使用预处理宏用或不用模板。

1
投票

这是未发布软件,但它似乎BOOST_ENUM弗兰克·劳布可能适合该法案。我喜欢它的部分是,你可以在类大部分基于宏枚举通常不会让你做的范围内定义枚举。它坐落于Boost库在:toto/MyEnum_to_string.h toto/MyEnum_to_string.cpp toto/MyEnum2_to_string.h toto/MyEnum2_to_string.cpp 它没有看到任何的发展从2006年开始,所以我不知道如何将其与新的动力版本编译。下看库/测试使用的一个例子。


1
投票

我想张贴此万一有人发现它是有用的。

就我而言,我只是需要从一个单一的http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=&文件生成一个C ++ 11枚举ToString()FromString()功能。

我写的解析包含枚举项目的头文件,并在新的.hpp文件生成功能的python脚本。

您可以使用的CMakeLists.txt添加.cpp这个脚本,或在Visual Studio预生成事件。该execute_process文件将被自动生成,无需手动更新每次添加一个新的枚举项一次。

generate_enum_strings.朋友

.cpp

例:

ErrorCode.hpp

# This script is used to generate strings from C++ enums

import re
import sys
import os

fileName = sys.argv[1]
enumName = os.path.basename(os.path.splitext(fileName)[0])

with open(fileName, 'r') as f:
    content = f.read().replace('\n', '')

searchResult = re.search('enum(.*)\{(.*?)\};', content)
tokens = searchResult.group(2)
tokens = tokens.split(',')
tokens = map(str.strip, tokens)
tokens = map(lambda token: re.search('([a-zA-Z0-9_]*)', token).group(1), tokens)

textOut = ''
textOut += '\n#include "' + enumName + '.hpp"\n\n'
textOut += 'namespace myns\n'
textOut += '{\n'
textOut += '    std::string ToString(ErrorCode errorCode)\n'
textOut += '    {\n'
textOut += '        switch (errorCode)\n'
textOut += '        {\n'

for token in tokens:
    textOut += '        case ' + enumName + '::' + token + ':\n'
    textOut += '            return "' + token + '";\n'

textOut += '        default:\n'
textOut += '            return "Last";\n'
textOut += '        }\n'
textOut += '    }\n'
textOut += '\n'
textOut += '    ' + enumName + ' FromString(const std::string &errorCode)\n'
textOut += '    {\n'
textOut += '        if ("' + tokens[0] + '" == errorCode)\n'
textOut += '        {\n'
textOut += '            return ' + enumName + '::' + tokens[0] + ';\n'
textOut += '        }\n'

for token in tokens[1:]:
    textOut += '        else if("' + token + '" == errorCode)\n'
    textOut += '        {\n'
    textOut += '            return ' + enumName + '::' + token + ';\n'
    textOut += '        }\n'

textOut += '\n'
textOut += '        return ' + enumName + '::Last;\n'
textOut += '    }\n'
textOut += '}\n'

fileOut = open(enumName + '.cpp', 'w')
fileOut.write(textOut)

运行#pragma once #include <string> #include <cstdint> namespace myns { enum class ErrorCode : uint32_t { OK = 0, OutOfSpace, ConnectionFailure, InvalidJson, DatabaseFailure, HttpError, FileSystemError, FailedToEncrypt, FailedToDecrypt, EndOfFile, FailedToOpenFileForRead, FailedToOpenFileForWrite, FailedToLaunchProcess, Last }; std::string ToString(ErrorCode errorCode); ErrorCode FromString(const std::string &errorCode); }

结果:

ErrorCode.cpp

python generate_enum_strings.py ErrorCode.hpp

1
投票

你可以使用反射库,像#include "ErrorCode.hpp" namespace myns { std::string ToString(ErrorCode errorCode) { switch (errorCode) { case ErrorCode::OK: return "OK"; case ErrorCode::OutOfSpace: return "OutOfSpace"; case ErrorCode::ConnectionFailure: return "ConnectionFailure"; case ErrorCode::InvalidJson: return "InvalidJson"; case ErrorCode::DatabaseFailure: return "DatabaseFailure"; case ErrorCode::HttpError: return "HttpError"; case ErrorCode::FileSystemError: return "FileSystemError"; case ErrorCode::FailedToEncrypt: return "FailedToEncrypt"; case ErrorCode::FailedToDecrypt: return "FailedToDecrypt"; case ErrorCode::EndOfFile: return "EndOfFile"; case ErrorCode::FailedToOpenFileForRead: return "FailedToOpenFileForRead"; case ErrorCode::FailedToOpenFileForWrite: return "FailedToOpenFileForWrite"; case ErrorCode::FailedToLaunchProcess: return "FailedToLaunchProcess"; case ErrorCode::Last: return "Last"; default: return "Last"; } } ErrorCode FromString(const std::string &errorCode) { if ("OK" == errorCode) { return ErrorCode::OK; } else if("OutOfSpace" == errorCode) { return ErrorCode::OutOfSpace; } else if("ConnectionFailure" == errorCode) { return ErrorCode::ConnectionFailure; } else if("InvalidJson" == errorCode) { return ErrorCode::InvalidJson; } else if("DatabaseFailure" == errorCode) { return ErrorCode::DatabaseFailure; } else if("HttpError" == errorCode) { return ErrorCode::HttpError; } else if("FileSystemError" == errorCode) { return ErrorCode::FileSystemError; } else if("FailedToEncrypt" == errorCode) { return ErrorCode::FailedToEncrypt; } else if("FailedToDecrypt" == errorCode) { return ErrorCode::FailedToDecrypt; } else if("EndOfFile" == errorCode) { return ErrorCode::EndOfFile; } else if("FailedToOpenFileForRead" == errorCode) { return ErrorCode::FailedToOpenFileForRead; } else if("FailedToOpenFileForWrite" == errorCode) { return ErrorCode::FailedToOpenFileForWrite; } else if("FailedToLaunchProcess" == errorCode) { return ErrorCode::FailedToLaunchProcess; } else if("Last" == errorCode) { return ErrorCode::Last; } return ErrorCode::Last; } } 。您注册枚举,然后你可以将它们转换来回的API。

Ponder

0
投票

这几乎是可以做到的(字符串数组也可以工作)的唯一途径。

问题是,一旦一个C程序编译时,枚举的二进制值是所有被使用,并且名字也没有了。


0
投票

这是我写的轻松枚举转换为字符串,一个CLI程序。它易于使用,大约需要5秒钟完成它(包括时间到CD包含程序的目录,然后运行它,传递给它包含的枚举的文件)。

这里下载:enum class MyEnum { Zero = 0, One = 1, Two = 2 }; ponder::Enum::declare<MyEnum>() .value("Zero", MyEnum::Zero) .value("One", MyEnum::One) .value("Two", MyEnum::Two); ponder::EnumObject zero(MyEnum::Zero); zero.name(); // -> "Zero"

在这里讨论主题:http://www.mediafire.com/?nttignoozzz

用“--help”参数运行程序得到说明如何使用它。


0
投票

不久前,我做了一些技巧,有枚举正确显示在QComboBox,并具有枚举和字符串表示的定义为一个语句

http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

现在你已经得到#pragma once #include <boost/unordered_map.hpp> namespace enumeration { struct enumerator_base : boost::noncopyable { typedef boost::unordered_map<int, std::wstring> kv_storage_t; typedef kv_storage_t::value_type kv_type; kv_storage_t const & kv() const { return storage_; } LPCWSTR name(int i) const { kv_storage_t::const_iterator it = storage_.find(i); if(it != storage_.end()) return it->second.c_str(); return L"empty"; } protected: kv_storage_t storage_; }; template<class T> struct enumerator; template<class D> struct enum_singleton : enumerator_base { static enumerator_base const & instance() { static D inst; return inst; } }; } #define QENUM_ENTRY(K, V, N) K, N storage_.insert(std::make_pair((int)K, V)); #define QBEGIN_ENUM(NAME, C) \ enum NAME \ { \ C \ } \ }; \ } \ #define QEND_ENUM(NAME) \ }; \ namespace enumeration \ { \ template<> \ struct enumerator<NAME>\ : enum_singleton< enumerator<NAME> >\ { \ enumerator() \ { //usage /* QBEGIN_ENUM(test_t, QENUM_ENTRY(test_entry_1, L"number uno", QENUM_ENTRY(test_entry_2, L"number dos", QENUM_ENTRY(test_entry_3, L"number tres", QEND_ENUM(test_t))))) */ 能枚举转换为字符串。如果您有enumeration::enum_singleton<your_enum>::instance()替换kv_storage_t,你也可以做反向转换。转炉公共基类被引入到其存放在Qt的对象,因为Qt的对象不可能是模板

boost::bimap


0
投票

作为变例,使用简单的lib> Previous appearance

在代码

http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

加行

#include <EnumString.h>

enum FORM {
    F_NONE = 0,
    F_BOX,
    F_CUBE,
    F_SPHERE,
};

做工精细,如果枚举值不重复。

用法示例

Begin_Enum_String( FORM )
{
    Enum_String( F_NONE );
    Enum_String( F_BOX );
    Enum_String( F_CUBE );
    Enum_String( F_SPHERE );
}
End_Enum_String;

反之亦然

enum FORM f = ...
const std::string& str = EnumString< FORM >::From( f );

0
投票

下面是一个尝试只用了一行宏命令自动获得<<和>>流运营商的枚举...

定义:

assert( EnumString< FORM >::To( f, str ) );

用法:

#include <string>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>

#define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__)
#define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__)
#define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__)
#define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__)
#define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__)
#define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__)
#define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__)
#define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__)
#define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__)
#define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__)
#define MAKE_STRING10_(str) #str

#define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__)
#define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__)

#define MAKE_ENUM_(attribute, name, ...) name { __VA_ARGS__ }; \
    attribute std::istream& operator>>(std::istream& is, name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        std::string str; \
        std::istream& r = is >> str; \
        const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
        const std::vector<std::string> enumStr(name##Str, name##Str + len); \
        const std::vector<std::string>::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \
        if (it != enumStr.end())\
            e = name(it - enumStr.begin()); \
        else \
            throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \
        return r; \
    }; \
    attribute std::ostream& operator<<(std::ostream& os, const name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        return (os << name##Str[e]); \
    }

不知道这个方案虽然的限制......意见,欢迎!


41
投票

@hydroo:如果没有额外的文件:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)

#define MAKE_ENUM(VAR) VAR,
enum MetaSyntacticVariable{
    SOME_ENUM(MAKE_ENUM)
};

#define MAKE_STRINGS(VAR) #VAR,
const char* const MetaSyntacticVariableNames[] = {
    SOME_ENUM(MAKE_STRINGS)
};

0
投票
// Declare global enum
enum MAKE_ENUM(Test3, Item13, Item23, Item33, Itdsdgem43);

class Essai {
public:
    // Declare enum inside class
    enum MAKE_CLASS_ENUM(Test, Item1, Item2, Item3, Itdsdgem4);

};

int main() {
    std::cout << Essai::Item1 << std::endl;

    Essai::Test ddd = Essai::Item1;
    std::cout << ddd << std::endl;

    std::istringstream strm("Item2");
    strm >> ddd;

    std::cout << (int) ddd << std::endl;
    std::cout << ddd << std::endl;
}

33
投票

我倾向于做的就是创建一个C数组中相同的顺序和位置枚举值的名称。

例如。

enum colours { red, green, blue };
const char *colour_names[] = { "red", "green", "blue" };

那么你可以使用的地方数组,你想要一个人类可读的值,例如

colours mycolour = red;
cout << "the colour is" << colour_names[mycolour];

你可以尝试用一下字符串化操作符(见#您的预处理器参考),将做你想做的,在某些情况─例如:

#define printword(XX) cout << #XX;
printword(red);

将打印“红”到标准输出。不幸的是它不会变的工作(如你会得到变量名打印出来)


9
投票

我有一个令人难以置信的简单易用的宏,这是否完全干燥的方式。它涉及复杂的宏和一些简单的解析魔术。开始:

#define AWESOME_MAKE_ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT}; \
inline std::ostream& operator<<(std::ostream& os, name value) { \
std::string enumName = #name; \
std::string str = #__VA_ARGS__; \
int len = str.length(); \
std::vector<std::string> strings; \
std::ostringstream temp; \
for(int i = 0; i < len; i ++) { \
if(isspace(str[i])) continue; \
        else if(str[i] == ',') { \
        strings.push_back(temp.str()); \
        temp.str(std::string());\
        } \
        else temp<< str[i]; \
} \
strings.push_back(temp.str()); \
os << enumName << "::" << strings[static_cast<int>(value)]; \
return os;} 

要在代码中使用这一点,只需做:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);

8
投票

QT是能够实现自己的(感谢元对象编译器)的:link


7
投票

我今天重新发明了这个轮,我想我会分享。

此实现不要求定义的常量,它可以是枚举或#defines或其他任何转予整数代码做任何改动 - 在我的情况,我在其他符号的定义的符号。它也有稀疏值效果很好。它甚至允许多个名字相同的值,总是返回第一个。唯一的缺点是,它需要你做出的常量,随着新的添加,例如可能成为外的日期表。

struct IdAndName
{
   int          id;
   const char * name;
   bool operator<(const IdAndName &rhs) const { return id < rhs.id; }
};
#define ID_AND_NAME(x) { x, #x }

const char * IdToName(int id, IdAndName *table_begin, IdAndName *table_end)
{
   if ((table_end - table_begin) > 1 && table_begin[0].id > table_begin[1].id)
      std::stable_sort(table_begin, table_end);

   IdAndName searchee = { id, NULL };
   IdAndName *p = std::lower_bound(table_begin, table_end, searchee);
   return (p == table_end || p->id != id) ? NULL : p->name;
}

template<int N>
const char * IdToName(int id, IdAndName (&table)[N])
{
   return IdToName(id, &table[0], &table[N]);
}

你会如何使用它的一个例子:

static IdAndName WindowsErrorTable[] =
{
   ID_AND_NAME(INT_MAX),               // flag value to indicate unsorted table
   ID_AND_NAME(NO_ERROR),
   ID_AND_NAME(ERROR_INVALID_FUNCTION),
   ID_AND_NAME(ERROR_FILE_NOT_FOUND),
   ID_AND_NAME(ERROR_PATH_NOT_FOUND),
   ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES),
   ID_AND_NAME(ERROR_ACCESS_DENIED),
   ID_AND_NAME(ERROR_INVALID_HANDLE),
   ID_AND_NAME(ERROR_ARENA_TRASHED),
   ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY),
   ID_AND_NAME(ERROR_INVALID_BLOCK),
   ID_AND_NAME(ERROR_BAD_ENVIRONMENT),
   ID_AND_NAME(ERROR_BAD_FORMAT),
   ID_AND_NAME(ERROR_INVALID_ACCESS),
   ID_AND_NAME(ERROR_INVALID_DATA),
   ID_AND_NAME(ERROR_INVALID_DRIVE),
   ID_AND_NAME(ERROR_CURRENT_DIRECTORY),
   ID_AND_NAME(ERROR_NOT_SAME_DEVICE),
   ID_AND_NAME(ERROR_NO_MORE_FILES)
};

const char * error_name = IdToName(GetLastError(), WindowsErrorTable);

IdToName功能依赖于std::lower_bound做快速查找,这需要进行排序表。如果表中的前两个条目都失灵,该功能将自动排序。

编辑:注释让我想到使用相同原理的另一种方式。宏简化了大switch声明的产生。

#define ID_AND_NAME(x) case x: return #x

const char * WindowsErrorToName(int id)
{
    switch(id)
    {
        ID_AND_NAME(ERROR_INVALID_FUNCTION);
        ID_AND_NAME(ERROR_FILE_NOT_FOUND);
        ID_AND_NAME(ERROR_PATH_NOT_FOUND);
        ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES);
        ID_AND_NAME(ERROR_ACCESS_DENIED);
        ID_AND_NAME(ERROR_INVALID_HANDLE);
        ID_AND_NAME(ERROR_ARENA_TRASHED);
        ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY);
        ID_AND_NAME(ERROR_INVALID_BLOCK);
        ID_AND_NAME(ERROR_BAD_ENVIRONMENT);
        ID_AND_NAME(ERROR_BAD_FORMAT);
        ID_AND_NAME(ERROR_INVALID_ACCESS);
        ID_AND_NAME(ERROR_INVALID_DATA);
        ID_AND_NAME(ERROR_INVALID_DRIVE);
        ID_AND_NAME(ERROR_CURRENT_DIRECTORY);
        ID_AND_NAME(ERROR_NOT_SAME_DEVICE);
        ID_AND_NAME(ERROR_NO_MORE_FILES);
        default: return NULL;
    }
}

6
投票
#define stringify( name ) # name

enum MyEnum {
    ENUMVAL1
};
...stuff...

stringify(EnumName::ENUMVAL1);  // Returns MyEnum::ENUMVAL1

Further discussion on this method

Preprocessor directive tricks for newcomers


6
投票

这可以在C ++ 11做

#include <map>
enum MyEnum { AA, BB, CC, DD };

static std::map< MyEnum, const char * > info = {
   {AA, "This is an apple"},
   {BB, "This is a book"},
   {CC, "This is a coffee"},
   {DD, "This is a door"}
};

void main()
{
    std::cout << info[AA] << endl
              << info[BB] << endl
              << info[CC] << endl
              << info[DD] << endl;
}
© www.soinside.com 2019 - 2024. All rights reserved.