2008-10-14 2140 views
104

假設我們有一些命名的枚舉:有沒有簡單的方法將C++枚舉轉換爲字符串?

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中,因此使用到目前爲止提出的解決方案不是一種選擇。

+0

可能的重複[簡單的方法來使用枚舉類型的變量作爲C中的字符串?](http://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types- as-string-in-c) – karlphillip 2011-07-02 23:26:52

+0

關於基於宏的工廠移動到http://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types-as-string-in- c#202511 - 問題更新後,它在這裏不再相關。 – Suma 2008-10-14 15:42:21

回答

45

你可能想看看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標籤並生成所需的代碼的任何語言。

+0

非常棒!作爲一個簡單的Python腳本的魅力。謝謝。 – 2008-10-14 19:43:00

+6

+1,GCCXML看起來非常好! (雖然我幾乎-1,因爲我最初誤以爲這是一個建議,使用上面詳細的XML語法來編碼您的枚舉 - 一個解決方案,它是過度工程!) – 2009-03-12 09:46:02

0

這幾乎是它可以完成的唯一方法(一個字符串數組也可以工作)。

問題是,一旦C程序編譯完成,枚舉的二進制值就是所有使用的,並且名稱不見了。

29

我傾向於做的是創建一個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); 

將打印「紅色」到標準輸出。不幸的是它不能用於變量(因爲你會得到打印的變量名)

+0

最後一個警告(不適用於變量)是一個很大的缺點,但無論如何+1。 – chappjc 2015-08-27 17:55:12

+2

僅在您不會爲枚舉條目設置特殊數值時有效。 – kyb 2017-08-27 13:46:27

1

回答0的一個問題是枚舉二進制值不一定從0開始,並且不一定是連續的。

當我需要這個,我通常:

  • 拉枚舉定義爲我的源
  • 編輯它得到的只是名稱
  • 做一個宏的名稱更改爲的情況下條款這個問題,儘管通常在一行上:case foo:return「foo」;
  • 添加開關,默認及其它語法,使其合法
68

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

顏色。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. 
+11

漂亮, 雖然我不喜歡額外的文件 – 2008-10-14 18:02:57

+0

+1,但可能會混淆Intellisense和類似的東西...... – 2008-10-14 18:26:02

+2

只要確保您的構建過程不會在每個包含之前預先包含#pragma(一次)文件... – xtofl 2008-10-14 18:47:58

3

Suma's macro solution很不錯。不過,你不需要有兩個不同的宏。 C++會高興地包含一個頭兩次。只留下包括警衛。

所以你必須定義只是

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

的foobar.h中,你將包括這樣的:

#define ENUMFACTORY_ARGUMENT "foobar.h" 
#include "enumfactory.h" 

enumfactory.h會做2 #include ENUMFACTORY_ARGUMENT秒。在第一輪中,它擴大了ENUM,如Suma的DECLARE_ENUM;在第二輪ENUM的工作方式如DEFINE_ENUM

可以包括enumfactory.h多次,也只要你在不同#通過定義對ENUMFACTORY_ARGUMENT

+0

似乎像suma移動答案[這裏](https://stackoverflow.com/questions/147267/easy-way-to-use - 變量-的-枚舉類型-AS-字符串在-C#202511)。你可能想要在你的答案中包含鏈接。我只是偶然發現了這個評論,並且嘲笑了這個回答這個是相當無意義的 – user463035818 2017-06-29 15:38:10

2

請注意,您的轉換函數理想情況下應該返回一個const char *。

如果你能負擔得起把你枚舉了各自的頭文件,你也許可以做這樣的事情與宏(哦,這將是醜陋的):

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

其中enum_def.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 }; 

而且enum_conv.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"; } } 

最後,colour.h有:

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

而且你可以使用轉換函數爲:

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

這是醜陋的,但它是唯一的方法(在預處理級),可以讓你定義只在一個地方在你的枚舉你的代碼。因此,您的代碼不容易因爲對枚舉進行修改而出錯。您的枚舉定義和轉換函數將始終保持同步。但是,我再說一遍,這很醜陋:)

1

以下ruby腳本嘗試解析標題,並將原始標題旁邊的所需源構建。

#! /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)) 
    } 
    } 
} 

使用正則表達式使得這個「解析器」非常脆弱,它可能無法正常處理您的特定頭文件。

比方說,你有一個頭toto/a.h,包含枚舉MyEnum和MyEnum2的定義。該腳本將建立:

toto/MyEnum_to_string.h 
toto/MyEnum_to_string.cpp 
toto/MyEnum2_to_string.h 
toto/MyEnum2_to_string.cpp 

更強大的解決方案是:

  • 構建所有源定義從另一個源枚舉和他們的行動。這意味着你將在一個XML/YML /任何文件中定義你的枚舉,這比C/C++更容易解析。
  • 使用真正的編譯器,如Avdi建議的。
  • 使用帶或不帶模板的預處理器宏。
2

另一個答案:在某些情況下,定義非代碼格式的枚舉是有意義的,例如CSV,YAML或XML文件,然後生成C++枚舉代碼和to-string代碼來自定義。這種方法在您的應用程序中可能會或可能不會實用,但應該記住這一點。

8

QT是能拉那(感謝元對象編譯器)的:link

2

我這樣做有其與宏生成單獨的並排的側枚舉包裝類。有幾個優點:

  • 可以生成它們枚舉我不定義(如:操作系統平臺頭枚舉)
  • 可以將檢查範圍爲包裝類
  • 可以做「聰明」與格式位字段枚舉

當然,缺點是我需要在格式化程序類中複製枚舉值,並且我沒有任何腳本來生成它們。除此之外,它似乎工作得很好。

下面是從我的代碼庫,SANS所有的框架代碼中的枚舉它實現了宏和模板的例子,但你可以得到的想法:

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; 

然後我們的想法是不是使用EHelpLocation,你使用SEHelpLocation;一切工作原理都是一樣的,但是你可以在枚舉變量本身上得到範圍檢查和'Format()'方法。如果您需要格式化獨立值,則可以使用CEnumFormatter_EHelpLocation :: FormatEnum(...)。

希望這是有幫助的。我意識到這也沒有解決關於腳本實際生成其他類的原始問題,但是我希望這個結構可以幫助試圖解決相同問題的人,或者編寫這樣的腳本。

38

@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) 
}; 
5

饒有興趣的看着路數。這裏是我很久以前用過的一個:

in 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; 
} 
1

這是未發佈軟件,但它似乎BOOST_ENUM弗蘭克·勞布可能適合該法案。我喜歡的部分是,你可以在一個類的範圍內定義一個枚舉,這個類的大部分基於宏的枚舉通常不允許你這樣做。它位於Boost Vault中:http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& 自2006年以來它沒有看到任何發展,所以我不知道它如何編譯新的Boost版本。 查看libs/test下的使用示例。

0

這是我寫的一個CLI程序,可以輕鬆將枚舉轉換爲字符串。 它易於使用,並需要大約5秒鐘完成(包括時間cd到包含該程序的目錄,然後運行它,傳遞給它包含枚舉的文件)。這裏 http://www.mediafire.com/?nttignoozzz

討論主題就可以了:

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

運行與「--help」的說法程序可以讓說明如何使用它。

7

今天我剛剛重新發明了這個輪子,並認爲我會分享它。

此實現執行需要對定義的常量,它可以是枚舉或#define S或其他任何轉予整數代碼做任何改動 - 在我的情況我已經在其他符號的定義的符號。它也適用於稀疏值。它甚至允許多個名稱具有相同的值,並始終返回第一個名稱。唯一的缺點是它需要你創建一個常量表,當新的例子被添加時,這些常量表可能會過時。

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

不久前,我做了一些技巧,有枚舉正確顯示在QComboBox,並具有枚舉和字符串表示的定義爲一個語句

#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()能夠將枚舉轉換爲字符串。如果您用boost::bimap替換kv_storage_t,您也可以進行反向轉換。轉爐 公共基類被引入到其存放在Qt的對象,因爲Qt的對象不可能是模板

Previous appearance

0

作爲變例,使用簡單的lib>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中的值不是dublicate

用法示例

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

,反之亦然

assert(EnumString<FORM>::To(f, str)); 
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); 
0

這裏是一個企圖得到關於電子< <和>>流運營商自動只有一行宏命令NUM ...

定義:

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

用法:

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

不知道這個方案的侷限性,但...評論,歡迎!

0
#include <iostream> 
#include <map> 
#define IDMAP(x) (x,#x) 

std::map<int , std::string> enToStr; 
class mapEnumtoString 
{ 
public: 
    mapEnumtoString(){ } 
    mapEnumtoString& operator()(int i,std::string str) 
    { 
     enToStr[i] = str; 
     return *this; 
    } 
public: 
    std::string operator [] (int i) 
    { 
     return enToStr[i]; 
    } 

}; 
mapEnumtoString k; 
mapEnumtoString& init() 
{ 
    return k; 
} 

int main() 
{ 

init() 
    IDMAP(1) 
    IDMAP(2) 
    IDMAP(3) 
    IDMAP(4) 
    IDMAP(5); 
std::cout<<enToStr[1]; 
std::cout<<enToStr[2]; 
std::cout<<enToStr[3]; 
std::cout<<enToStr[4]; 
std::cout<<enToStr[5]; 
} 
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 
); 
3

這是@修改user3360260答案。它具有以下新的特點

  • MyEnum fromString(const string&)支持
  • 編譯與VisualStudio的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; } \ 
};   

注意轉換爲字符串是一種快速查找,而從字符串轉換爲慢速線性搜索。但字符串反正非常昂貴(和相關的文件IO),我不覺得需要優化或使用bimap。

4

這可以在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; 
} 
2

這裏一個文件解決方案來完成(基於優雅的答案被@Marcin:

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

這是我與升壓解決方案:

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

轉換:

SERVICE_RELOAD serviceReloadEnum = strToEnum<SERVICE_RELOAD>("reload_log"); 
QString serviceReloadStr = enumToStr(reload_log); 
0

我想發佈這個萬一有人認爲它有用。

就我而言,我只是需要從一個單一的.hpp文件生成一個C++ 11枚舉ToString()FromString()功能。

我寫了一個python腳本,用於解析包含枚舉項的頭文件,並在新的.cpp文件中生成函數。

您可以使用execute_process在CMakeLists.txt中添加此腳本,或者將其作爲Visual Studio中的預生成事件。 .cpp文件將自動生成,無需在每次添加新的枚舉項目時手動更新它。

generate_enum_strings。PY

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

實施例:

ErrorCode.hpp

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

運行python generate_enum_strings.py ErrorCode.hpp

結果:

ErrorCode.cpp

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

那麼,還有一個選擇。一個典型的用例是你需要HTTP動詞的常量,以及使用字符串版本值。

的例子:

int main() { 

    VERB a = VERB::GET; 
    VERB b = VERB::GET; 
    VERB c = VERB::POST; 
    VERB d = VERB::PUT; 
    VERB e = VERB::DELETE; 


    std::cout << a.toString() << std::endl; 

    std::cout << a << std::endl; 

    if (a == VERB::GET) { 
    std::cout << "yes" << std::endl; 
    } 

    if (a == b) { 
    std::cout << "yes" << std::endl; 
    } 

    if (a != c) { 
    std::cout << "no" << std::endl; 
    } 

} 

動詞類:

// ----------------------------------------------------------- 
// ----------------------------------------------------------- 
class VERB { 

private: 

    // private constants 
    enum Verb {GET_=0, POST_, PUT_, DELETE_}; 

    // private string values 
    static const std::string theStrings[]; 

    // private value 
    const Verb value; 
    const std::string text; 

    // private constructor 
    VERB (Verb v) : 
    value(v), text (theStrings[v]) 
    { 
    // std::cout << " constructor \n"; 
    } 

public: 

    operator const char *() const { return text.c_str(); } 

    operator const std::string() const { return text; } 

    const std::string toString() const { return text; } 

    bool operator == (const VERB & other) const { return (*this).value == other.value; } 

    bool operator != (const VERB & other) const { return ! ((*this) == other); } 

    // --- 

    static const VERB GET; 
    static const VERB POST; 
    static const VERB PUT; 
    static const VERB DELETE; 

}; 

const std::string VERB::theStrings[] = {"GET", "POST", "PUT", "DELETE"}; 

const VERB VERB::GET = VERB (VERB::Verb::GET_); 
const VERB VERB::POST = VERB (VERB::Verb::POST_); 
const VERB VERB::PUT = VERB (VERB::Verb::PUT_); 
const VERB VERB::DELETE = VERB (VERB::Verb::DELETE_); 
// end of file 
0

使用化合物三元語句可以是用於與幾個要素(單行)枚舉幾分優雅。表達式的長度也僅隨元素的數量而近似線性增長。

這裏有一個很好的用例:

enum log_level {INFO, WARNING, ERROR}; 
... 
void logger::write(const std::string log, const log_level l) { 
    ... 
    std::string s = (l == INFO) ? "INFO" : 
        (l == WARNING) ? "WARNING" : 
        (l == ERROR) ? "ERROR" : "UNKNOWN"; 
    ... 
} 
... 

當然,它只是一個開關/ if語句塊,但它是一個單行的語句。作爲簡單與簡單的問題,它在中間的某個地方遇到。作爲一個常量表達式,它也可以很容易地變成內聯函數。

相關問題