Cách dễ dàng ánh xạ enum c ++ thành chuỗi


119

Tôi có một loạt các kiểu enum trong một số tệp tiêu đề thư viện mà tôi đang sử dụng và tôi muốn có cách chuyển đổi giá trị enum thành chuỗi người dùng - và ngược lại.

RTTI sẽ không làm điều đó cho tôi, bởi vì 'chuỗi người dùng' cần phải dễ đọc hơn một chút so với bảng liệt kê.

Một giải pháp brute force sẽ là một loạt các chức năng như thế này, nhưng tôi cảm thấy nó hơi giống C.

enum MyEnum {VAL1, VAL2,VAL3};

String getStringFromEnum(MyEnum e)
{
  switch e
  {
  case VAL1: return "Value 1";
  case VAL2: return "Value 2";
  case VAL1: return "Value 3";
  default: throw Exception("Bad MyEnum");
  }
}

Tôi có cảm giác ruột gan rằng có một giải pháp thanh lịch bằng cách sử dụng các mẫu, nhưng tôi chưa thể hiểu rõ về nó.

CẬP NHẬT: Cảm ơn bạn đã đề xuất - Tôi nên nói rõ rằng các enum được định nghĩa trong tiêu đề thư viện của bên thứ ba, vì vậy tôi không muốn phải thay đổi định nghĩa của chúng.

Cảm giác ruột của tôi bây giờ là tránh các khuôn mẫu và làm điều gì đó như sau:

char * MyGetValue(int v, char *tmp); // implementation is trivial

#define ENUM_MAP(type, strings) char * getStringValue(const type &T) \
 { \
 return MyGetValue((int)T, strings); \
 }

; enum eee {AA,BB,CC}; - exists in library header file 
; enum fff {DD,GG,HH}; 

ENUM_MAP(eee,"AA|BB|CC")
ENUM_MAP(fff,"DD|GG|HH")

// To use...

    eee e;
    fff f;
    std::cout<< getStringValue(e);
    std::cout<< getStringValue(f);

Câu trả lời:


60

Nếu bạn muốn tên enum dưới dạng chuỗi, hãy xem bài đăng này . Nếu không, a std::map<MyEnum, char const*>sẽ hoạt động tốt. (Không ích gì khi sao chép chuỗi ký tự của bạn sang chuỗi std :: trong bản đồ)

Để có thêm cú pháp, đây là cách viết một lớp map_init. Mục tiêu là cho phép

std::map<MyEnum, const char*> MyMap;
map_init(MyMap)
    (eValue1, "A")
    (eValue2, "B")
    (eValue3, "C")
;

Hàm template <typename T> map_init(T&)trả về a map_init_helper<T>. map_init_helper<T>lưu trữ một T &, và xác định tầm thường map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&). (Quay trở lại *thistừ operator()cho phép chuỗi operator(), như operator<<trên std::ostreams)

template<typename T> struct map_init_helper
{
    T& data;
    map_init_helper(T& d) : data(d) {}
    map_init_helper& operator() (typename T::key_type const& key, typename T::mapped_type const& value)
    {
        data[key] = value;
        return *this;
    }
};

template<typename T> map_init_helper<T> map_init(T& item)
{
    return map_init_helper<T>(item);
}

Vì hàm và lớp trợ giúp được tạo khuôn mẫu, bạn có thể sử dụng chúng cho bất kỳ bản đồ nào hoặc cấu trúc giống như bản đồ. Tức là nó cũng có thể thêm các mục vàostd::unordered_map

Nếu bạn không thích viết những trợ giúp này, boost :: gán cung cấp chức năng tương tự.


Bạn đúng khi đề cập đến một câu hỏi khác. Mọi người nên có một cái nhìn trong "câu hỏi liên quan" trước khi đăng tải ...
xtofl

2
@xtofl: "Các câu hỏi liên quan" hiển thị ở đây hoàn toàn khác với các câu hỏi liên quan được liệt kê khi tôi đăng câu hỏi!
Roddy

@MSalters, std :: map là một cách hữu ích để xử lý việc triển khai, nhưng tôi đang tìm một số cách để giảm bớt mã viết sẵn có thể yêu cầu.
Roddy

@MSalters, thật tuyệt nếu có thể chấp nhận nhiều đối số cho toán tử []. nhưng đáng buồn thay, người ta không thể làm điều đó. x [a, b] đánh giá thành x [b]. biểu thức (a, b) sử dụng toán tử dấu phẩy. vì vậy nó tương đương với ["A"] ["B"] ["C"] trong mã của bạn. bạn có thể thay đổi nó thành [eValue1] ["A"] [eValu ..
Johannes Schaub - litb

toán tử lệnh gọi hàm cũng sẽ là một ứng cử viên sáng giá: map_init (MyMap) (eValue1, "A") (eValue2, "B") .... thì nó tương đương với boost :: gán: insert (MyMap) (eValue1, "A") (eValue2, "B") ... ( boost.org/doc/libs/1_35_0/libs/assign/doc/index.html )
Johannes Schaub - litb

31

Giải pháp MSalters là một giải pháp tốt nhưng về cơ bản phải thực hiện lại boost::assign::map_list_of. Nếu bạn có boost, bạn có thể sử dụng nó trực tiếp:

#include <boost/assign/list_of.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>

using boost::assign::map_list_of;

enum eee { AA,BB,CC };

const boost::unordered_map<eee,const char*> eeeToString = map_list_of
    (AA, "AA")
    (BB, "BB")
    (CC, "CC");

int main()
{
    std::cout << " enum AA = " << eeeToString.at(AA) << std::endl;
    return 0;
}

Bạn sẽ sử dụng điều này như thế nào khi eeeToString là thành viên dữ liệu của một lớp? Tôi nhận được "Lỗi: khởi tạo thành viên dữ liệu không được phép"
Người dùng

@ Người dùng: Các thành viên dữ liệu lớp được khởi tạo trong các hàm khởi tạo, thường là trong danh sách bộ khởi tạo.
MSalters

Có cách nào để làm cho điều này hoạt động cho tất cả các enums. Tôi có nhiều khai báo enum và không muốn bản đồ chỉ hoạt động cho loại eeetrong trường hợp của bạn.
Justin Liang

Tôi đã cố gắng sử dụng một mẫu nhưng sau đó đã và lỗi: error: template declaration of 'const boost::unordered::unordered_map<T, const char*> enumToString'.
Justin Liang

4
Trên thực tế, câu trả lời này phần lớn đã lỗi thời với C ++ 11.
Alastair

19

Tự động tạo một biểu mẫu từ một biểu mẫu khác.

Nguồn:

enum {
  VALUE1, /* value 1 */
  VALUE2, /* value 2 */
};

Đã tạo:

const char* enum2str[] = {
  "value 1", /* VALUE1 */
  "value 2", /* VALUE2 */
};

Nếu các giá trị enum lớn thì một biểu mẫu được tạo có thể sử dụng bản đồ chưa có thứ tự <> hoặc các mẫu như được Constantin đề xuất.

Nguồn:

enum State{
  state0 = 0, /* state 0 */
  state1 = 1, /* state 1 */
  state2 = 2, /* state 2 */
  state3 = 4, /* state 3 */

  state16 = 0x10000, /* state 16 */
};

Đã tạo:

template <State n> struct enum2str { static const char * const value; };
template <State n> const char * const enum2str<n>::value = "error";

template <> struct enum2str<state0> { static const char * const value; };
const char * const enum2str<state0>::value = "state 0";

Thí dụ:

#include <iostream>

int main()
{
  std::cout << enum2str<state16>::value << std::endl;
  return 0;
}

Mặc dù nhanh nhất nhưng nó không dễ dàng như @MSalters.
kenny

2
Đó là nếu bạn có một chút perl / python để đọc danh sách các chuỗi từ tệp văn bản và tạo tệp .h với ký tự tĩnh tại thời điểm biên dịch. = "Viết chương trình để viết chương trình"
Martin Beckett

@mgb: perl / python không phải là các tùy chọn duy nhất mà hầu như bất kỳ công cụ tạo mẫu nào bằng bất kỳ ngôn ngữ nào cũng sẽ thực hiện (trong trường hợp này, một công cụ tạo cả hai biểu mẫu từ một mẫu).
jfs

@jf. Có, điểm quan trọng là tự động xây dựng các bảng dữ liệu tĩnh tại thời điểm biên dịch. Tôi có lẽ chỉ muốn tạo một mảng tĩnh câm.
Martin Beckett

Điều này sẽ hoạt động nếu State không được biết tại thời điểm biên dịch? Tôi khá chắc chắn rằng nó sẽ không - về lý thuyết, trình biên dịch sẽ phải khởi tạo mẫu enum2str với tất cả các giá trị có thể có của enum, điều mà tôi khá chắc chắn rằng gcc (ít nhất) sẽ không làm được.
Alastair

11

Tôi nhớ đã trả lời điều này ở bất kỳ nơi nào khác trên StackOverflow. Lặp lại nó ở đây. Về cơ bản, đó là một giải pháp dựa trên các macro đa dạng và khá dễ sử dụng:

#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 nó trong mã của bạn, chỉ cần thực hiện:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
auto dog = Animal::DOG;
std::cout<<dog;

1
Chỉ cần thay đổi khai báo lớp enum thành enum để hoạt động trên pre c ++ 11.
Debdatta Basu

1
Bạn nói đúng, nó hoạt động (tự động cũng chỉ là c ++ 11). Giải pháp tốt! Nó sẽ là hoàn hảo nếu bạn cũng có thể thiết lập giá trị cho một số enums
jamk

Tôi đoán tôi đã thấy trong một cái gì đó tăng cường như vậy
Sergei

10

Tôi đề xuất kết hợp sử dụng X-macro là giải pháp tốt nhất và các chức năng mẫu sau:

Cho mượn marcinkoziukmyopenidcom và gia hạn

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

template <class T> T str2enum( const char* );
template <class T> const char* enum2str( T );

#define STR2ENUM(TYPE,ARRAY) \
template <> \
TYPE str2enum<TYPE>( const char* str ) \
    { \
    for( int i = 0; i < (sizeof(ARRAY)/sizeof(ARRAY[0])); i++ ) \
        if( !strcmp( ARRAY[i], str ) ) \
            return TYPE(i); \
    return TYPE(0); \
    }

#define ENUM2STR(TYPE,ARRAY) \
template <> \
const char* enum2str<TYPE>( TYPE v ) \
    { \
    return ARRAY[v]; \
    }

#define ENUMANDSTR(TYPE,ARRAY)\
    STR2ENUM(TYPE,ARRAY) \
    ENUM2STR(TYPE,ARRAY)

ENUMANDSTR(Colours,colours_str)

colour.def

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

Có cách nào để làm cho định nghĩa mảng chuỗi enum chung chung không? (Tôi không biết làm thế nào để xử lý một bên X-Macro một macro và tôi không xử lý các mẫu dễ dàng)
Jonathan

5

Tôi sử dụng giải pháp này mà tôi tái tạo bên dưới:

#define MACROSTR(k) #k

#define X_NUMBERS \
       X(kZero  ) \
       X(kOne   ) \
       X(kTwo   ) \
       X(kThree ) \
       X(kFour  ) \
       X(kMax   )

enum {
#define X(Enum)       Enum,
    X_NUMBERS
#undef X
} kConst;

static char *kConstStr[] = {
#define X(String) MACROSTR(String),
    X_NUMBERS
#undef X
};

int main(void)
{
    int k;
    printf("Hello World!\n\n");

    for (k = 0; k < kMax; k++)
    {
        printf("%s\n", kConstStr[k]);
    }

    return 0;
}

1
Đây là Macro X cơ bản, và tôi rất ngạc nhiên rằng đây là câu trả lời đầu tiên ở đây để gợi ý nó! +1
Lightness Races in Orbit

4

Nếu bạn muốn lấy biểu diễn chuỗi của MyEnum các biến , thì các mẫu sẽ không cắt nó. Mẫu có thể chuyên biệt về các giá trị tích phân đã biết tại thời điểm biên dịch.

Tuy nhiên, nếu đó là những gì bạn muốn thì hãy thử:

#include <iostream>

enum MyEnum { VAL1, VAL2 };

template<MyEnum n> struct StrMyEnum {
    static char const* name() { return "Unknown"; }
};

#define STRENUM(val, str) \
  template<> struct StrMyEnum<val> { \
    static char const* name() { return str; }};

STRENUM(VAL1, "Value 1");
STRENUM(VAL2, "Value 2");

int main() {
  std::cout << StrMyEnum<VAL2>::name();
}

Điều này là dài dòng, nhưng sẽ bắt gặp các lỗi như lỗi bạn đã thực hiện trong câu hỏi - của bạn case VAL1bị trùng lặp.


Thực ra tên phương thức () là không cần thiết. Hãy xem câu trả lời của tôi.
jfs 16/10/08

3

Tôi đã dành nhiều thời gian hơn để nghiên cứu chủ đề này mà tôi muốn thừa nhận. May mắn thay, có những giải pháp nguồn mở tuyệt vời trong tự nhiên.

Đây là hai cách tiếp cận tuyệt vời, ngay cả khi chưa đủ nổi tiếng (chưa),

Wisdom_enum

  • Thư viện enum thông minh độc lập cho C ++ 11/14/17. Nó hỗ trợ tất cả các chức năng tiêu chuẩn mà bạn mong đợi từ một lớp enum thông minh trong C ++.
  • Hạn chế: yêu cầu ít nhất C ++ 11.

Enums tốt hơn

  • Thư viện enum thời gian biên dịch phản chiếu với cú pháp rõ ràng, trong một tệp tiêu đề duy nhất và không có phụ thuộc.
  • Hạn chế: dựa trên macro, không thể sử dụng bên trong một lớp.

2

Tôi muốn có một bản đồ m - và nhúng cái này vào enum.

thiết lập với m [MyEnum.VAL1] = "Giá trị 1";

và tất cả đã xong.


2

Tôi đã yêu cầu chức năng này nhiều lần để gỡ lỗi / phân tích mã từ những người khác. Đối với điều này, tôi đã viết một tập lệnh Perl tạo ra một lớp với một số toStringphương thức được nạp chồng . Mỗi toStringphương thức nhận Enummột đối số và trả vềconst char* .

Tất nhiên, tập lệnh không phân tích cú pháp C ++ cho chính enums, mà sử dụng ctags để tạo bảng biểu tượng.

Tập lệnh Perl ở đây: http://heinitz-it.de/download/enum2string/enum2string.pl.html


2

Câu trả lời của bạn đã truyền cảm hứng cho tôi để tự viết một số macro. Yêu cầu của tôi như sau:

  1. chỉ viết mỗi giá trị của enum một lần, vì vậy không có danh sách kép để duy trì

  2. không giữ các giá trị enum trong một tệp riêng biệt mà sau này là #included, vì vậy tôi có thể viết nó ở bất cứ đâu tôi muốn

  3. không thay thế chính enum, tôi vẫn muốn định nghĩa kiểu enum, nhưng thêm vào đó, tôi muốn có thể ánh xạ mọi tên enum thành chuỗi tương ứng (để không ảnh hưởng đến mã kế thừa)

  4. việc tìm kiếm phải nhanh chóng, vì vậy tốt nhất là không có trường hợp chuyển mạch, đối với những môi trường khổng lồ đó

Mã này tạo một enum cổ điển với một số giá trị. Ngoài ra, nó tạo ra dưới dạng std :: map ánh xạ từng giá trị enum với tên của nó (tức là map [E_SUNDAY] = "E_SUNDAY", v.v.)

Ok, đây là mã bây giờ:

EnumUtilsImpl.h :

map<int, string> & operator , (map<int, string> & dest, 
                               const pair<int, string> & keyValue) {
    dest[keyValue.first] = keyValue.second; 
    return dest;
}

#define ADD_TO_MAP(name, value) pair<int, string>(name, #name)

EnumUtils.h // đây là tệp bạn muốn đưa vào bất cứ khi nào bạn cần thực hiện công việc này, bạn sẽ sử dụng các macro từ nó:

#include "EnumUtilsImpl.h"
#define ADD_TO_ENUM(name, value) \
    name value

#define MAKE_ENUM_MAP_GLOBAL(values, mapName) \
    int __makeMap##mapName() {mapName, values(ADD_TO_MAP); return 0;}  \
    int __makeMapTmp##mapName = __makeMap##mapName();

#define MAKE_ENUM_MAP(values, mapName) \
    mapName, values(ADD_TO_MAP);

MyProjectCodeFile.h // đây là một ví dụ về cách sử dụng nó để tạo một enum tùy chỉnh:

#include "EnumUtils.h*

#define MyEnumValues(ADD) \
    ADD(val1, ), \
    ADD(val2, ), \
    ADD(val3, = 100), \
    ADD(val4, )

enum MyEnum {
    MyEnumValues(ADD_TO_ENUM)
};

map<int, string> MyEnumStrings;
// this is how you initialize it outside any function
MAKE_ENUM_MAP_GLOBAL(MyEnumValues, MyEnumStrings); 

void MyInitializationMethod()
{ 
    // or you can initialize it inside one of your functions/methods
    MAKE_ENUM_MAP(MyEnumValues, MyEnumStrings); 
}

Chúc mừng.


2

Đâ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!


1

trong tiêu đề:

enum EFooOptions
 {
FooOptionsA = 0, EFooOptionsMin = 0,
FooOptionsB,
FooOptionsC,
FooOptionsD 
EFooOptionsMax
};
extern const wchar* FOO_OPTIONS[EFooOptionsMax];

trong tệp .cpp:

const wchar* FOO_OPTIONS[] = {
    L"One",
    L"Two",
    L"Three",
    L"Four"
};

Lưu ý: Không xử lý chỉ mục mảng xấu. :) Nhưng bạn có thể dễ dàng thêm một hàm để xác minh enum trước khi lấy chuỗi từ mảng.


Quả thực là một giải pháp rất KHÔNG KHÔ.
xtofl 16/10/08

bây giờ bạn đề cập đến DRY. tệp .h và .cpp được tạo tự động từ một số tệp đầu vào khác. Tôi muốn thấy các giải pháp tốt hơn (không sử dụng đến sự phức tạp không cần thiết)
moogs 16/10/08

1

Tôi chỉ muốn hiển thị giải pháp thanh lịch khả thi này bằng cách sử dụng macro. Điều này không giải quyết được vấn đề nhưng tôi nghĩ rằng đó là một cách tốt để suy nghĩ lại về vấn đề.

#define MY_LIST(X) X(value1), X(value2), X(value3)

enum eMyEnum
    {
    MY_LIST(PLAIN)
    };

const char *szMyEnum[] =
    {
    MY_LIST(STRINGY)
    };


int main(int argc, char *argv[])
{

std::cout << szMyEnum[value1] << value1 <<" " <<  szMyEnum[value2] << value2 << std::endl;

return 0;
}

---- BIÊN TẬP ----

Sau một số nghiên cứu trên internet và một số giải thích riêng, tôi đã đi đến giải pháp sau:

//this is the enum definition
#define COLOR_LIST(X) \
  X( RED    ,=21)      \
  X( GREEN  )      \
  X( BLUE   )      \
  X( PURPLE , =242)      \
  X( ORANGE )      \
  X( YELLOW )

//these are the macros
#define enumfunc(enums,value) enums,
#define enumfunc2(enums,value) enums value,
#define ENUM2SWITCHCASE(enums) case(enums): return #enums;

#define AUTOENUM(enumname,listname) enum enumname{listname(enumfunc2)};
#define ENUM2STRTABLE(funname,listname) char* funname(int val) {switch(val) {listname(ENUM2SWITCHCASE) default: return "undef";}}
#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int values[] = {listname(enumfunc)};int N = sizeof(values)/sizeof(int);ENUM2STRTABLE(enum2str,listname)};

//here the enum and the string enum map table are generated
AUTOENUM(testenum,COLOR_LIST)
ENUM2STRTABLE(testfunenum,COLOR_LIST)
ENUM2STRUCTINFO(colorinfo,COLOR_LIST)//colorinfo structur {int values[]; int N; char * enum2str(int);}

//debug macros
#define str(a) #a
#define xstr(a) str(a)


int main( int argc, char** argv )
{
testenum x = YELLOW;
std::cout << testfunenum(GREEN) << "   " << testfunenum(PURPLE) << PURPLE << "  " << testfunenum(x);

for (int i=0;i< colorinfo::N;i++)
std::cout << std::endl << colorinfo::values[i] <<  "  "<< colorinfo::enum2str(colorinfo::values[i]);

  return EXIT_SUCCESS;
}

Tôi chỉ muốn đăng nó có thể ai đó có thể tìm thấy giải pháp này hữu ích. Không cần các lớp mẫu, không cần c ++ 11 và không cần tăng cường, vì vậy điều này cũng có thể được sử dụng cho C.

---- EDIT2 ----

bảng thông tin có thể tạo ra một số vấn đề khi sử dụng nhiều hơn 2 enum (vấn đề trình biên dịch). Cách giải quyết sau đã hoạt động:

#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int spacename##_##values[] = {listname(enumfunc)};int spacename##_##N = sizeof(spacename##_##values)/sizeof(int);ENUM2STRTABLE(spacename##_##enum2str,listname)};

1
typedef enum {
    ERR_CODE_OK = 0,
    ERR_CODE_SNAP,

    ERR_CODE_NUM
} ERR_CODE;

const char* g_err_msg[ERR_CODE_NUM] = {
    /* ERR_CODE_OK   */ "OK",
    /* ERR_CODE_SNAP */ "Oh, snap!",
};

Trên đây là giải pháp đơn giản của tôi. Một lợi ích của nó là 'NUM' kiểm soát kích thước của mảng thông báo, nó cũng ngăn chặn truy cập ngoài ranh giới (nếu bạn sử dụng nó một cách khôn ngoan).

Bạn cũng có thể xác định một hàm để lấy chuỗi:

const char* get_err_msg(ERR_CODE code) {
    return g_err_msg[code];
}

Xa hơn về giải pháp của tôi, sau đó tôi thấy giải pháp sau đây khá thú vị. Nó thường giải quyết được vấn đề đồng bộ của cái trên.

Trang trình bày tại đây: http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

Mã tại đây: https://github.com/arunksaha/enum_to_string


1

Tôi biết mình dự tiệc muộn, nhưng đối với những người khác truy cập trang này, bạn có thể thử điều này, nó dễ dàng hơn mọi thứ ở đó và có ý nghĩa hơn:

namespace texs {
    typedef std::string Type;
    Type apple = "apple";
    Type wood = "wood";
}

Bạn có đề xuất sử dụng chuỗi và không sử dụng enum không? Điều đó không thực sự giải quyết được vấn đề.
Roddy

0

Gần đây tôi đã gặp vấn đề tương tự với thư viện của nhà cung cấp (Fincad). May mắn thay, nhà cung cấp đã cung cấp tính năng nhân đôi xml cho tất cả các enum. Tôi đã kết thúc việc tạo một bản đồ cho mỗi loại enum và cung cấp một hàm tra cứu cho mỗi loại enum. Kỹ thuật này cũng cho phép bạn chặn một tra cứu bên ngoài phạm vi của enum.

Tôi chắc chắn rằng swig có thể làm điều gì đó tương tự cho bạn, nhưng tôi rất vui khi cung cấp các utils tạo mã được viết bằng ruby.

Đây là một mẫu mã:

std::map<std::string, switches::FCSW2::type> init_FCSW2_map() {
        std::map<std::string, switches::FCSW2::type> ans;
        ans["Act365Fixed"] = FCSW2::Act365Fixed;
        ans["actual/365 (fixed)"] = FCSW2::Act365Fixed;
        ans["Act360"] = FCSW2::Act360;
        ans["actual/360"] = FCSW2::Act360;
        ans["Act365Act"] = FCSW2::Act365Act;
        ans["actual/365 (actual)"] = FCSW2::Act365Act;
        ans["ISDA30360"] = FCSW2::ISDA30360;
        ans["30/360 (ISDA)"] = FCSW2::ISDA30360;
        ans["ISMA30E360"] = FCSW2::ISMA30E360;
        ans["30E/360 (30/360 ISMA)"] = FCSW2::ISMA30E360;
        return ans;
}
switches::FCSW2::type FCSW2_lookup(const char* fincad_switch) {
        static std::map<std::string, switches::FCSW2::type> switch_map = init_FCSW2_map();
        std::map<std::string, switches::FCSW2::type>::iterator it = switch_map.find(fincad_switch);
        if(it != switch_map.end()) {
                return it->second;
        } else {
                throw FCSwitchLookupError("Bad Match: FCSW2");
        }
}

Có vẻ như bạn muốn đi theo cách khác (enum thành chuỗi, chứ không phải chuỗi thành enum), nhưng điều này sẽ nhỏ để đảo ngược.

- Vớ vẩn


1
a) Có ai khác thấy điều này hoàn toàn không thể đọc được không? Một vài typedef và sử dụng khai báo sẽ cải thiện đáng kể khả năng đọc. b) khai báo tĩnh cục bộ không an toàn cho luồng. c) sử dụng chuỗi const & thay vì char *, d) bao gồm giá trị không thể tìm thấy trong ngoại lệ được ném ra thì sao?
Alastair

0

Xem liệu cú pháp sau có phù hợp với bạn không:

// WeekEnd enumeration
enum WeekEnd
{
    Sunday = 1,
    Saturday = 7
};

// String support for WeekEnd
Begin_Enum_String( WeekEnd )
{
    Enum_String( Sunday );
    Enum_String( Saturday );
}
End_Enum_String;

// Convert from WeekEnd to string
const std::string &str = EnumString<WeekEnd>::From( Saturday );
// str should now be "Saturday"

// Convert from string to WeekEnd
WeekEnd w;
EnumString<WeekEnd>::To( w, "Sunday" );
// w should now be Sunday

Nếu có, thì bạn có thể muốn xem bài viết này:
http://www.gamedev.net/reference/snippets/features/cppstringizing/


0

mớ hỗn độn cũ đúng này là nỗ lực của tôi dựa trên các bit và lựa chọn từ SO. For_each sẽ phải được mở rộng để hỗ trợ hơn 20 giá trị enum. Đã thử nghiệm nó trên visual studio 2019, clang và gcc. c ++ 11

#define _enum_expand(arg) arg
#define _enum_select_for_each(_,_0, _1, _2,_3,_4, _5, _6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,N, ...) N
#define _enum_for_each_0(_call, arg0,arg1,...)
#define _enum_for_each_1(_call, arg0,arg1) _call(arg0,arg1)
#define _enum_for_each_2(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_1(_call,arg0, __VA_ARGS__))
#define _enum_for_each_3(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_2(_call,arg0, __VA_ARGS__))
#define _enum_for_each_4(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_3(_call,arg0, __VA_ARGS__))
#define _enum_for_each_5(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_4(_call,arg0, __VA_ARGS__))
#define _enum_for_each_6(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_5(_call,arg0, __VA_ARGS__))
#define _enum_for_each_7(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_6(_call,arg0, __VA_ARGS__))
#define _enum_for_each_8(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_7(_call,arg0, __VA_ARGS__))
#define _enum_for_each_9(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_8(_call,arg0, __VA_ARGS__))
#define _enum_for_each_10(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_9(_call,arg0, __VA_ARGS__))
#define _enum_for_each_11(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_10(_call,arg0, __VA_ARGS__))
#define _enum_for_each_12(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_11(_call,arg0, __VA_ARGS__))
#define _enum_for_each_13(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_12(_call,arg0, __VA_ARGS__))
#define _enum_for_each_14(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_13(_call,arg0, __VA_ARGS__))
#define _enum_for_each_15(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_14(_call,arg0, __VA_ARGS__))
#define _enum_for_each_16(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_15(_call,arg0, __VA_ARGS__))
#define _enum_for_each_17(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_16(_call,arg0, __VA_ARGS__))
#define _enum_for_each_18(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_17(_call,arg0, __VA_ARGS__))
#define _enum_for_each_19(_call, arg0,arg1, ...) _call(arg) _enum_expand(_enum_for_each_18(_call,arg0, __VA_ARGS__))
#define _enum_for_each(arg, ...) \
    _enum_expand(_enum_select_for_each(_, ##__VA_ARGS__, \
    _enum_for_each_19, _enum_for_each_18, _enum_for_each_17, _enum_for_each_16, _enum_for_each_15, \
    _enum_for_each_14, _enum_for_each_13, _enum_for_each_12, _enum_for_each_11, _enum_for_each_10, \
    _enum_for_each_9,  _enum_for_each_8,  _enum_for_each_7,  _enum_for_each_6,  _enum_for_each_5,  \
    _enum_for_each_4,  _enum_for_each_3,  _enum_for_each_2,  _enum_for_each_1,  _enum_for_each_0)(arg, ##__VA_ARGS__))

#define _enum_strip_args_1(arg0) arg0
#define _enum_strip_args_2(arg0, arg1) arg0, arg1
#define _enum_make_args(...) (__VA_ARGS__)

#define _enum_elem_arity1_1(arg) arg,
#define _enum_elem_arity1( ...) _enum_expand(_enum_elem_arity1_1 __VA_ARGS__)
#define _enum_elem_arity2_1(arg0,arg1) arg0 = arg1,
#define _enum_elem_arity2( ...) _enum_expand(_enum_elem_arity2_1 __VA_ARGS__)

#define _enum_elem_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_elem_select_arity_1(...) _enum_expand(_enum_elem_select_arity_2(__VA_ARGS__, _enum_elem_arity2,_enum_elem_arity1,_))
#define _enum_elem_select_arity(enum_type,...) _enum_expand(_enum_elem_select_arity_1 __VA_ARGS__)(__VA_ARGS__)

#define _enum_str_arity1_1(enum_type,arg) { enum_type::arg,#arg },
#define _enum_str_arity1(enum_type,...) _enum_expand(_enum_str_arity1_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_1 __VA_ARGS__)))
#define _enum_str_arity2_1(enum_type,arg,value) { enum_type::arg,#arg },
#define _enum_str_arity2(enum_type, ...) _enum_expand(_enum_str_arity2_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_2 __VA_ARGS__)))
#define _enum_str_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_str_select_arity_1(...) _enum_expand(_enum_str_select_arity_2(__VA_ARGS__, _enum_str_arity2,_enum_str_arity1,_))
#define _enum_str_select_arity(enum_type,...) _enum_expand(_enum_str_select_arity_1 __VA_ARGS__)(enum_type,__VA_ARGS__)

#define error_code_enum(enum_type,...)  enum class enum_type {              \
    _enum_expand(_enum_for_each(_enum_elem_select_arity,enum_type, ##__VA_ARGS__))};  \
    namespace _ ## enum_type ## _detail { \
        template <typename> struct _ ## enum_type ## _error_code{ \
            static const std::map<enum_type, const char*> enum_type ## _map; \
        }; \
            template <typename T> \
            const std::map<enum_type, const char*> _ ## enum_type ## _error_code<T>::enum_type ## _map = { \
                _enum_expand(_enum_for_each(_enum_str_select_arity,enum_type,  ##__VA_ARGS__)) \
        }; \
    } \
    inline const char* get_error_code_name(const enum_type& value) { \
        return _ ## enum_type ## _detail::_ ## enum_type ## _error_code<enum_type>::enum_type ## _map.find(value)->second; \
    } 

error_code_enum(myenum,
    (one, 1),
    (two)
);

tạo ra mã sau

enum class myenum { 
    one = 1,
    two,
};
namespace _myenum_detail {
    template <typename>
    struct _myenum_error_code {
        static const std::map<myenum, const char*> myenum_map;
    };
    template <typename T>
    const std::map<myenum, const char*> _myenum_error_code<T>::myenum_map = {
        { myenum::one, "one" }, 
        { myenum::two, "two" },
    };
}
inline const char* get_error_code_name(const myenum& value) { 
    return _myenum_detail::_myenum_error_code<myenum>::myenum_map.find(value)->second; 
}

Thật xấu hổ, bạn phải nhảy dù với bộ tiền xử lý để thực hiện điều này bằng một trong những ngôn ngữ lập trình được sử dụng nhiều nhất trên thế giới ...


0

Bằng cách sử dụng các bộ khởi tạo mảng được chỉ định, mảng chuỗi của bạn độc lập với thứ tự của các phần tử trong enum:

enum Values {
    Val1,
    Val2
};

constexpr string_view v_name[] = {
    [Val1] = "Value 1",
    [Val2] = "Value 2"
}
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.