Có cách nào đơn giản để chuyển đổi C ++ enum thành chuỗi không?


123

Giả sử chúng ta có một số enums được đặt tên:

enum MyEnum {
      FOO,
      BAR = 0x50
};

Những gì tôi sử dụng trên googled là một tập lệnh (bất kỳ ngôn ngữ nào) quét tất cả các tiêu đề trong dự án của tôi và tạo một tiêu đề với một hàm trên mỗi enum.

char* enum_to_string(MyEnum t);

Và một triển khai với một cái gì đó như thế này:

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

Gotcha thực sự là với enum kiểu định dạng và enum kiểu C không tên. Có ai biết một cái gì đó cho điều này?

CHỈNH SỬA: Giải pháp không nên sửa đổi nguồn của tôi, ngoại trừ các chức năng đã tạo. Các enums nằm trong một API, vì vậy việc sử dụng các giải pháp được đề xuất cho đến bây giờ không phải là một lựa chọn.


Câu trả lời về nhà máy dựa trên Macro được chuyển vào stackoverflow.com/questions/147267/… - sau khi câu hỏi được cập nhật, nó không còn phù hợp ở đây nữa.
Suma 14/10/08

Câu trả lời:


48

Bạn có thể muốn xem GCCXML .

Chạy GCCXML trên mã mẫu của bạn sẽ tạo ra:

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

Bạn có thể sử dụng bất kỳ ngôn ngữ nào bạn muốn để lấy ra các thẻ Enumeration và EnumValue và tạo mã mong muốn của bạn.


Thông minh! Làm việc như một sự quyến rũ với một tập lệnh python đơn giản. Cảm ơn.
Edu Felipe

6
+1, GCCXML trông rất đẹp! (Mặc dù tôi gần như -1ed như tôi ban đầu không nhận định đây là một gợi ý để sử dụng trên verbose cú pháp XML để mã hóa enum của bạn - một giải pháp mà sặc mùi overengineering)
j_random_hacker

1
bất kỳ thay đổi nào bạn có thể đăng script python?
phillipwei

74

X-macro là giải pháp tốt nhất. Thí dụ:

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

Tuy nhiên, tôi thường thích phương pháp sau hơn, để có thể điều chỉnh chuỗi một chút.

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

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

11
tiện lợi, mặc dù tôi không thích tệp bổ sung
Ronny Brendel 14/10/08

2
Chỉ cần chắc chắn quá trình xây dựng của bạn không #pragma không thêm vào trước (một lần) trước mỗi tập tin bao gồm ...
xtofl

24
Tôi không chắc về giải pháp "tốt nhất"!
Lightness Races in Orbit

2
Giải pháp này hoàn toàn vượt trội so với bất kỳ trường hợp chuyển mạch hoặc dựa trên mảng nào, vì nó không trùng lặp tên, giúp dễ dàng thay đổi kiểu liệt kê.
Julien Guertault

2
@ ikku100 bạn không chính xác #define X(a, b) #b. Điều này chỉ cần thiết nếu định nghĩa trông giống như thế này X(Red, red), thay vì định nghĩa được hiển thị trong câu trả lời,X(Red, "red")
learningvst

43

@hydroo: Không có tệp bổ sung:

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

Tôi thích giải pháp này. Tuy nhiên, sẽ rõ ràng hơn nếu SOME_UNION và MAKE_UNION được gọi là SOME_ENUM và MAKE_ENUM.
Bruno Martinez

Đây là một giải pháp tuyệt vời. Tôi có trình quản lý tài nguyên C ++ dễ bảo trì nhất mà tôi từng xử lý.
DCurro

Tôi phải cảm ơn bạn cho giải pháp đơn giản này :-) - Tôi đã sửa đổi nó một chút tuy nhiên, để có MetaSyntacticVariableNames[]một phần được của một khai báo lớp, bằng cách làm cho một phương phápstatic const char* getNameByEnum(MetaSyntacticVariable e) { /*code to return the static string*/ }
DeckerDK

Câu trả lời tuyệt vời! Tôi đã đơn giản hóa nó hơn nữa bằng cách nhóm MAKE_ENUM và MAKE_STRINGS thành một macro duy nhất, làm cho toàn bộ quá trình trở nên đơn giản hơn. Tôi đã thêm một câu trả lời trong chủ đề này với mã đó nếu có ai quan tâm.
Francois Bertrand

35

Những gì tôi có xu hướng làm là tạo một mảng C với các tên theo thứ tự và vị trí giống như các giá trị enum.

ví dụ.

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

thì bạn có thể sử dụng mảng ở những nơi bạn muốn giá trị mà con người có thể đọc được, ví dụ:

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

Bạn có thể thử nghiệm một chút với toán tử xâu chuỗi (xem # trong tham chiếu bộ tiền xử lý của bạn) sẽ thực hiện những gì bạn muốn, trong một số trường hợp - ví dụ:

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

sẽ in "red" thành stdout. Thật không may, nó sẽ không hoạt động với một biến (vì bạn sẽ in tên biến)


Cảnh báo cuối cùng (sẽ không hoạt động với một biến) là một nhược điểm lớn, nhưng dù sao thì +1.
chappjc

3
Chỉ hoạt động nếu bạn không đặt các giá trị số đặc biệt cho các mục nhập enum.
kyb

11

Tôi có một macro cực kỳ đơn giản để sử dụng thực hiện điều này theo kiểu KHÔ hoàn toàn. Nó liên quan đến các macro đa dạng và một số phép thuật phân tích cú pháp đơn giản. Đây là:

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

Để sử dụng điều này trong mã của bạn, chỉ cần thực hiện:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);

1
Ý tưởng hay bằng cách sử dụng enum được gõ mạnh (lớp enum). Dưới đây là một bản demo: cpp.sh/4ife
chappjc

Điều này có hoạt động với các bảng liệt kê / ký hiệu được xác định bên ngoài. Ví dụ, hệ điều hành xác định hoặc thư viện xác định ký hiệu có khoảng trống trong đánh số?
Jason Harrison

Rất tốt, nhưng không biên dịch nếu đặt bên trong một lớp (tôi không thể tìm ra lý do tại sao).
AlwaysLearning

Tôi không thể lấy nó để biên dịch trong VS2015. Tôi nhận được cảnh báo và lỗi: warning: multi-line comment [-Wcomment] #define MAKE_ENUM (name, ...) enum class name { VA_ARGS , __COUNT} error: stray '#' trong chương trình std *: string enumName = #name
Craig.Feied

8

QT có thể kéo điều đó (nhờ trình biên dịch đối tượng meta):

QNetworkReply::NetworkError error;

error = fetchStuff();

if (error != QNetworkReply::NoError) {

    QString errorValue;

    QMetaObject meta = QNetworkReply::staticMetaObject;

    for (int i=0; i < meta.enumeratorCount(); ++i) {

        QMetaEnum m = meta.enumerator(i);

        if (m.name() == QLatin1String("NetworkError")) {

            errorValue = QLatin1String(m.valueToKey(error));

            break;

        }

    }

    QMessageBox box(QMessageBox::Information, "Failed to fetch",

                "Fetching stuff failed with error '%1`").arg(errorValue),

                QMessageBox::Ok);

    box.exec();

    return 1;

}

Trong Qt mọi lớp có macro Q_OBJECT sẽ tự động có một thành viên tĩnh "staticMetaObject" thuộc loại QMetaObject. Sau đó, bạn có thể tìm thấy tất cả những thứ thú vị như thuộc tính, tín hiệu, vị trí và thực sự là enums.

Nguồn


7

Điều này có thể được thực hiện trong 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;
}

1
Điều này không trả lời được câu hỏi của OP: anh ta đang tìm cách tự động tạo ra một hàm để trả về tên thành viên của enum dưới dạng một chuỗi.
Spooky

7

Hôm nay tôi mới phát minh lại bánh xe này và nghĩ rằng mình sẽ chia sẻ nó.

Việc triển khai này không yêu cầu bất kỳ thay đổi nào đối với mã xác định các hằng số, có thể là liệt kê hoặc#define s hoặc bất kỳ thứ gì khác chuyển thành số nguyên - trong trường hợp của tôi, tôi đã xác định các ký hiệu theo các ký hiệu khác. Nó cũng hoạt động tốt với các giá trị thưa thớt. Nó thậm chí còn cho phép nhiều tên cho cùng một giá trị, luôn trả về tên đầu tiên. Nhược điểm duy nhất là nó yêu cầu bạn tạo một bảng các hằng số, có thể trở nên lỗi thời khi các hằng số mới được thêm vào chẳng hạn.

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

Ví dụ về cách bạn sử dụng 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);

Các IdToName chức năng dựa vàostd::lower_bound để làm tra cứu nhanh chóng, đòi hỏi các bảng được sắp xếp. Nếu hai mục đầu tiên trong bảng không theo thứ tự, hàm sẽ tự động sắp xếp.

Chỉnh sửa: Một nhận xét khiến tôi nghĩ đến một cách khác để sử dụng cùng một nguyên tắc. Macro đơn giản hóa việc tạo một switchcâu lệnh lớn .

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

Giải pháp tốt. Nhưng đối với tôi, tôi thích switch and casenó hơn vì nó đơn giản và dễ hiểu.
Deqing

6
#define stringify( name ) # name

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

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

Thảo luận thêm về phương pháp này

Thủ thuật chỉ thị tiền xử lý cho người mới


4
Trên thực tế, điều này khá vô dụng, vì phương thức stringify đang ở thời điểm biên dịch và có nghĩa đen. Nếu bạn nói có kiểu enum được đề cập bên trong một biến, việc cố gắng xâu chuỗi biến đó sẽ chỉ cung cấp cho bạn tên biến chứ không phải tên kiểu enum.
srcspider

5

Thật thú vị khi xem số cách. đây là một trong những tôi đã sử dụng một thời gian dài trước đây:

trong tệp 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(){};
};

trong main.cpp

#include "myenummap.h"

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

Nó không phải const, nhưng nó thuận tiện.

Đây là một cách khác sử dụng các tính năng C ++ 11. Đây là const, không kế thừa vùng chứa STL và gọn gàng hơn một chút:

#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
Nó hoàn toàn hợp pháp. Tôi làm nó suốt.
Jonathan Graehl

Giải pháp tốt. Đây là c ++ nên dùng stl map là ok.
Adam Bruss

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();

Sử dụng:

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

1
Tôi thích API của bạn, nhưng thật không may, SmartEnum của bạn không thực sự tạo ra một "loại" enum. Bạn không thể làm được MyEnum x = MyEnum::TWO;. Tôi đã đăng bản chỉnh sửa của tôi về lớp học của bạn để hỗ trợ điều này.
Mark Lakata

4

Giải pháp vĩ mô của Suma rất hay. Tuy nhiên, bạn không cần phải có hai macro khác nhau. C ++ vui vẻ bao gồm một tiêu đề hai lần. Chỉ cần bỏ qua bảo vệ bao gồm.

Vì vậy, bạn sẽ có một foobar.h xác định

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

và bạn sẽ bao gồm nó như thế này:

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

enumfactory.h sẽ làm 2 #include ENUMFACTORY_ARGUMENT s. Trong vòng đầu tiên, nó mở rộng ENUM giống như của Suma DECLARE_ENUM; trong vòng thứ hai ENUM hoạt động như thế nào DEFINE_ENUM.

Bạn cũng có thể bao gồm enumfactory.h nhiều lần, miễn là bạn chuyển các # define khác nhau cho ENUMFACTORY_ARGUMENT


Có vẻ như suma đã chuyển câu trả lời đến đây . Bạn có thể muốn bao gồm liên kết trong câu trả lời của mình. Tôi chỉ tìm thấy những nhận xét một cách tình cờ và witout sumas trả lời là một trong những điều này khá vô nghĩa
idclev 463035818

3

Lưu ý rằng lý tưởng nhất là hàm chuyển đổi của bạn nên trả về một const char *.

Nếu bạn có đủ khả năng để đặt enum của mình trong các tệp tiêu đề riêng biệt của chúng, bạn có thể làm điều gì đó như thế này với macro (ồ, điều này sẽ rất tệ):

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

Trong đó enum_def.h có:

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

Và enum_conv.h có:

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

Và cuối cùng, colour.h có:

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

Và bạn có thể sử dụng chức năng chuyển đổi như:

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

Điều này thật tệ, nhưng đó là cách duy nhất (ở cấp bộ tiền xử lý) cho phép bạn xác định enum của mình chỉ ở một nơi duy nhất trong mã của bạn. Do đó, mã của bạn không dễ bị lỗi do sửa đổi enum. Định nghĩa enum của bạn và hàm chuyển đổi sẽ luôn đồng bộ. Tuy nhiên, tôi nhắc lại, điều này thật xấu xí :)


3

Một câu trả lời khác: trong một số ngữ cảnh, nên xác định kiểu liệt kê của bạn ở định dạng không phải mã, như tệp CSV, YAML hoặc XML, sau đó tạo cả mã liệt kê C ++ và mã chuỗi từ định nghĩa. Cách tiếp cận này có thể thực tế hoặc không thực tế trong ứng dụng của bạn, nhưng đó là điều cần ghi nhớ.


3

Đây là sửa đổi cho câu trả lời @ user3360260. Nó có các tính năng mới sau

  • MyEnum fromString(const string&) ủng hộ
  • biên dịch với VisualStudio 2012
  • enum là một kiểu POD thực tế (không chỉ là khai báo const), vì vậy bạn có thể gán nó cho một biến.
  • đã thêm tính năng "phạm vi" C ++ (dưới dạng vectơ) để cho phép lặp lại "foreach" trên enum

Sử dụng:

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

Đây là mã

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

Lưu ý rằng chuyển đổi sang Chuỗi là tìm kiếm nhanh, trong khi chuyển đổi từ Chuỗi là tìm kiếm tuyến tính chậm. Nhưng dù sao thì các chuỗi cũng rất đắt (và cả IO tệp liên quan), tôi không cảm thấy cần phải tối ưu hóa hoặc sử dụng một bimap.


Bạn và người dùng3360260 có một giải pháp tốt. Tại sao không có multimap để thay thế?
Vincent

3

Đây là giải pháp một tệp (dựa trên câu trả lời thanh lịch của @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

Tôi làm điều này với các lớp trình bao bọc enum cạnh nhau riêng biệt được tạo bằng macro. Có một số lợi thế:

  • Có thể tạo chúng cho các enums mà tôi không xác định (ví dụ: enums tiêu đề nền tảng hệ điều hành)
  • Có thể kết hợp kiểm tra phạm vi vào lớp trình bao bọc
  • Có thể thực hiện định dạng "thông minh hơn" với enums trường bit

Tất nhiên, nhược điểm là tôi cần sao chép các giá trị enum trong các lớp định dạng và tôi không có bất kỳ tập lệnh nào để tạo chúng. Ngoài ra, nó có vẻ hoạt động khá tốt.

Đây là một ví dụ về một enum từ codebase của tôi, không chứa tất cả mã khung triển khai các macro và mẫu, nhưng bạn có thể hiểu:

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;

Ý tưởng sau đó là thay vì sử dụng EHelpLocation, bạn sử dụng SEHelpLocation; mọi thứ hoạt động giống nhau, nhưng bạn nhận được kiểm tra phạm vi và phương thức 'Định dạng ()' trên chính biến enum. Nếu bạn cần định dạng một giá trị độc lập, bạn có thể sử dụng CEnumFormatter_EHelpLocation :: FormatEnum (...).

Hy vọng điều này là hữu ích. Tôi nhận ra rằng điều này cũng không giải quyết được câu hỏi ban đầu về một tập lệnh thực sự tạo ra lớp khác, nhưng tôi hy vọng cấu trúc này sẽ giúp ai đó đang cố gắng giải quyết vấn đề tương tự hoặc viết một tập lệnh như vậy.


2

Đó là phần mềm chưa được phát hành nhưng có vẻ như BOOST_ENUM từ Frank Laub có thể phù hợp với hóa đơn. Phần tôi thích ở nó là bạn có thể định nghĩa một enum trong phạm vi của một lớp mà hầu hết các enum dựa trên Macro thường không cho phép bạn làm. Nó nằm trong Boost Vault tại: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& Nó đã không thấy bất kỳ sự phát triển nào kể từ năm 2006 nên tôi không biết nó biên dịch tốt như thế nào với các bản phát hành Boost mới. Xem dưới libs / test để biết ví dụ về cách sử dụng.


2

Đây là giải pháp của tôi với BOOST:

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

Để tạo enum, hãy khai báo:

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

Đối với chuyển đổi:

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

2

Tôi muốn đăng điều này trong trường hợp ai đó thấy nó hữu ích.

Trong trường hợp của tôi, tôi chỉ cần tạo ToString()và các FromString()hàm cho một enum C ++ 11 từ một .hpptệp duy nhất .

Tôi đã viết một tập lệnh python phân tích cú pháp tệp tiêu đề chứa các mục enum và tạo các hàm trong một .cpptệp mới .

Bạn có thể thêm tập lệnh này trong CMakeLists.txt với execute_process hoặc dưới dạng sự kiện tạo trước trong Visual Studio. Các .cpptập tin sẽ được tự động tạo ra, mà không cần phải tự cập nhật nó mỗi lần một mục enum mới được thêm vào.

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

Thí dụ:

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

Chạy python generate_enum_strings.py ErrorCode.hpp

Kết quả:

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

1
Đây là một trình tạo trực tuyến: th-thielemann.de/tools/cpp-enum-to-string.html
Th. Thielemann

2

Thêm đơn giản hơn nữa trong việc sử dụng cho câu trả lời tuyệt vời của Jasper Bekkers :

Thiết lập một lần:

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

Sau đó, để sử dụng:

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

2

Bạn có thể sử dụng một thư viện phản chiếu, như Ponder . Bạn đăng ký enums và sau đó bạn có thể chuyển đổi chúng qua lại với API.

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"

1

Một vấn đề với câu trả lời 0 là các giá trị nhị phân enum không nhất thiết phải bắt đầu từ 0 và không nhất thiết phải liền kề.

Khi cần điều này, tôi thường:

  • kéo định nghĩa enum vào nguồn của tôi
  • chỉnh sửa nó để chỉ lấy tên
  • thực hiện một macro để thay đổi tên thành mệnh đề trường hợp trong câu hỏi, mặc dù thường nằm trên một dòng: case foo: return "foo";
  • thêm công tắc, mặc định và cú pháp khác để làm cho nó hợp pháp

1

Tập lệnh ruby ​​sau cố gắng phân tích cú pháp các tiêu đề và tạo các nguồn bắt buộc cùng với các tiêu đề ban đầu.

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

Việc sử dụng biểu thức chính quy làm cho "trình phân tích cú pháp" này khá mỏng manh, nó có thể không xử lý được các tiêu đề cụ thể của bạn một cách duyên dáng.

Giả sử bạn có một tiêu đề toto / ah, chứa các định nghĩa cho enums MyEnum và MyEnum2. Tập lệnh sẽ xây dựng:

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

Các giải pháp mạnh mẽ hơn sẽ là:

  • Xây dựng tất cả các nguồn xác định enum và hoạt động của chúng từ một nguồn khác. Điều này có nghĩa là bạn sẽ xác định enums của mình trong một tệp XML / YML / bất kỳ tệp nào dễ phân tích cú pháp hơn nhiều so với C / C ++.
  • Sử dụng một trình biên dịch thực như do Avdi đề xuất.
  • Sử dụng macro bộ xử lý trước có hoặc không có mẫu.

0

Đó là cách duy nhất có thể được thực hiện (một mảng chuỗi cũng có thể hoạt động).

Vấn đề là, khi một chương trình C được biên dịch, giá trị nhị phân của enum là tất cả những gì được sử dụng và tên sẽ biến mất.


0

Đây là một chương trình CLI tôi đã viết để dễ dàng chuyển đổi enum thành chuỗi. Nó dễ sử dụng và mất khoảng 5 giây để hoàn thành (bao gồm cả thời gian để cd vào thư mục chứa chương trình, sau đó chạy nó, chuyển cho nó tệp chứa enum).

Tải xuống tại đây: http://www.mediafire.com/?nttignoozzz

Chủ đề thảo luận về nó tại đây: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

Chạy chương trình với đối số "--help" để nhận mô tả cách sử dụng nó.


Bạn có thể vui lòng đặt nó trên một kho lưu trữ ở đâu đó (github, google code hoặc bitbucket) và đăng liên kết ở đây, thay vì mediafire không? Tôi sẽ giúp những người muốn hiểu nó :)
Edu Felipe

0

Cách đây không lâu, tôi đã thực hiện một số thủ thuật để hiển thị đúng enum trong QComboBox và định nghĩa về biểu diễn enum và chuỗi dưới dạng một câu lệnh

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

Bây giờ bạn đã có enumeration::enum_singleton<your_enum>::instance()thể chuyển đổi enum thành chuỗi. Nếu bạn thay thế kv_storage_tbằng boost::bimap, bạn cũng sẽ có thể thực hiện chuyển đổi ngược. Lớp cơ sở chung cho trình chuyển đổi được giới thiệu để lưu trữ nó trong đối tượng Qt, vì đối tượng Qt không thể là mẫu

Xuất hiện trước


0

Là biến thể, sử dụng lib đơn giản> http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

Trong mã

#include <EnumString.h>

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

thêm dòng

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

Làm việc tốt, nếu các giá trị trong enum không được công khai .

Ví dụ sử dụng

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

và ngược lại

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

0

Đây là một nỗ lực để lấy các toán tử dòng << và >> trên enum một cách tự động chỉ bằng một lệnh macro một dòng ...

Định nghĩa:

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

Sử dụng:

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

Không chắc chắn về những hạn chế của chương trình này mặc dù ... ý kiến ​​được hoan nghênh!


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

2
Vui lòng giải thích lý do tại sao đây là câu trả lời.
Alok
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.