2012-02-05 28 views

Trả lời

17

Trong khi điều này được thực hiện thường thông qua công tắc, tôi thích mảng:

#include <iostream> 

namespace foo { 
    enum Colors { BLUE = 0, RED, GREEN, SIZE_OF_ENUM }; 
    static const char* ColorNames[] = { "blue", "red", "green" }; 

    // statically check that the size of ColorNames fits the number of Colors 
    static_assert(sizeof(foo::ColorNames)/sizeof(char*) == foo::SIZE_OF_ENUM 
    , "sizes dont match"); 
} // foo 

int main() 
{ 
    std::cout << foo::ColorNames[foo::BLUE] << std::endl; 
    return 0; 
} 

Kích thước mảng rõ ràng có lợi ích của việc tạo ra một thời gian biên dịch lỗi nên kích thước của sự thay đổi enum và bạn quên thêm chuỗi thích hợp.

Ngoài ra, còn có Boost.Enum trong kho Boost. Thư viện chưa được chính thức phát hành nhưng khá ổn định và cung cấp những gì bạn muốn. Tôi sẽ không khuyên bạn nên nó đến một người mới mặc dù.

+3

Nó không tạo ra lỗi nếu bạn quên * thêm * chuỗi ... –

+1

@MatthieuM. Thật. Cách đáng tin cậy duy nhất để xây dựng một cái gì đó như thế này có lẽ là các macro. – pmr

+0

Tôi sợ như vậy. Tôi đã luôn luôn nghĩ rằng đó là một sự xấu hổ là tốt, tôi sẽ đánh giá cao trình biên dịch xây dựng các chức năng trên mặt. Một cách tôi tìm thấy là sử dụng macro, mặc dù điều này là inline chức năng nhiều hơn hoặc ít hơn. Cách khác là sử dụng các thử nghiệm để đảm bảo bản dịch đã hoạt động đối với các loại trước đó (ít nhất) và sau đó dựa vào nhà phát triển để * suy nghĩ * :) –

8

Điều đó vốn dĩ không thể.

Một C++ enum chỉ là một tập hợp các số có tên thời gian biên dịch.
Khi chạy, chúng không thể phân biệt được với các số thường.

Bạn cần viết câu lệnh switch trả về một chuỗi.

+1

cách hoạt động trong phản xạ C# ....? – CodingHero

+0

@CodingQuant: Có. Bạn có thể thấy sự phản chiếu trong nguồn của lớp 'Enum'. – SLaks

0

Bạn có thể lưu tên trong một chuỗi các chuỗi, được lập chỉ mục theo các giá trị enum.

enum Colours 
{ 
    Red =0, 
    Green=1, 
    Blue=2 
}; 

char* names[3] = {"Red", "Green", "Blue"}; 

Sau đó, bạn có thể in: "Invalid colour '" + names[colour] + "' selected."

Nhưng phương pháp này có thể không thể rất hữu ích nếu bạn không xác định enum giá trị liên tục. Trong trường hợp đó, cách tiếp cận này sẽ lãng phí bộ nhớ. Viết hàm có số switch trên giá trị enum sẽ hữu ích, như Alexander Gessler đã đề cập. Một giải pháp thay thế khác có thể là map từ STL.

1

Bạn phải làm điều đó bằng tay, ví dụ:

const char* ToString(Colours co) { 
    switch(co) { 
     case Red: 
      return "Red"; 
     // ... 
    } 
} 

Một bảng tra cứu cũng sẽ thực hiện được. Tôi cũng đã thấy mọi người sử dụng tập lệnh tùy chỉnh để tạo ra những thứ như vậy trên đầu trang của mã nguồn của họ.

3
enum Color 
{ 
    Red =0, 
    Green=1, 
    Blue=2 
}; 

std::string ColorMap[] = { "Red", "Green","Blue" }; 

Sử dụng ColorMap[c] để có được những đại diện chuỗi:

std::string msg = "Invalid colour '" + ColorMap[c] + "' selected."; 

Tuy nhiên, nếu các giá trị của enum là không liên tục, sau đó bạn có thể sử dụng std::map thay vì như:

enum Color 
{ 
    Red = 0x1, 
    Green = 0x2, 
    Blue = 0x4, 
    Black = 0x8, 
}; 

//C++11 only, as it uses std::initializer_list 
std::map<Color, std::string> ColorMap = { 
    {Red, "Red"}, 
    {Green, "Green"}, 
    {Blue, "Blue"}, 
    {Black, "Black"} 
}; 

//same as before! 
std::string msg = "Invalid colour '" + ColorMap[c] + "' selected."; 
+1

Theo tôi, giải pháp tốt nhất vì cách tiếp cận này cũng có thể được sử dụng với enums chứa khoảng trống. Ví dụ. Màu đỏ = 0x01, Xanh lục, Xanh dương = 0x10, Vàng. – Anonymous

0

Như @FlopCoder đã nói:

enum Colours 
{ 
    Red =0, 
    Green=1, 
    Blue=2 
}; 
char* ColourNames[] = { "Red", "Green", "Blue" }; 
int colour = Green; 
printf("Invalid colour '%s' selected.", ColourNames[ colour ]); 

Điều này tất nhiên sẽ chỉ hoạt động nếu enum của bạn bắt đầu từ 0 và liên tục.
Cách của @ Nawaz là nhiều hơn C++ phong cách.

+0

Hãy nhìn vào hạn chế kích thước của tôi. Điều này làm cho thành ngữ hơi đẹp hơn. – pmr

+0

Thật vậy, bạn của tôi. – Petruza

14

Làm thế nào về một chút ma thuật với các macro:

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


// http://stackoverflow.com/questions/236129/how-to-split-a-string-in-c 
std::vector<std::string> split(const std::string &text, char sep) { 
    std::vector<std::string> tokens; 
    int start = 0, end = 0; 
    while ((end = text.find(sep, start)) != std::string::npos) { 
     tokens.push_back(text.substr(start, end - start)); 
     start = end + 1; 
    } 
    tokens.push_back(text.substr(start)); 
    return tokens; 
} 

#define ENUM(name, ...)\ 
enum name \ 
{\ 
__VA_ARGS__\ 
};\ 
std::vector<std::string> name##Map = split(#__VA_ARGS__, ',');\ 
    std::string toString(const name v) { return name##Map.at(v);} 


ENUM(Color, Red,Green,Blue) 


int main(int c, char**v) 
{ 
    std::cout << toString(Red) << toString(Blue); 
    return 0;//a.exec(); 
} 

Vâng, tôi hiểu rằng đây là xấu xí và bạn muốn tốt hơn không làm những việc như vậy

+0

Xấu xí? Cái này thật tuyệt! –

+0

Tôi khá chắc chắn rằng điều này có thể được thực hiện sạch hơn với gói tham số C++ 11 - vì vậy bạn sẽ không có bất kỳ công việc nào trong thời gian chạy. – einpoklum

3

Tôi thực sự thích cách tiếp cận vĩ mô của @ Lol4t0 .

tôi mở rộng nó để có thể chuyển đổi một enum từ một chuỗi quá:

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

// http://stackoverflow.com/questions/236129/how-to-split-a-string-in-c 
std::vector<std::string> split(const std::string &text, char sep) { 
    std::vector<std::string> tokens; 
    int start = 0, end = 0; 
    while ((end = text.find(sep, start)) != std::string::npos) { 
     tokens.push_back(text.substr(start, end - start)); 
     start = end + 1; 
    } 
    tokens.push_back(text.substr(start)); 
    return tokens; 
} 

#define ENUM(name, ...)\ 
    enum name\ 
    {\ 
     __VA_ARGS__\ 
    };\ 
    static const int name##Size = (sizeof((int[]){__VA_ARGS__})/sizeof(int));\ 
    static const vector<string> name##ToStringMap = split(#__VA_ARGS__, ',');\ 
    const string name##ToString(const name value)\ 
    {\ 
     return name##ToStringMap.at(value);\ 
    };\ 
    map<string, name> name##ToFromStringMap(...)\ 
    {\ 
     map<string, name> m;\ 
     name args[name##Size] = { __VA_ARGS__ };\ 
     \ 
     int i;\ 
     for(i = 0; i < name##Size; ++i)\ 
     {\ 
      m[name##ToString(args[i])] = args[i];\ 
     }\ 
     return m;\ 
    };\ 
    static map<string, name> name##FromStringMap = name##ToFromStringMap(__VA_ARGS__);\ 
    const name name##FromString(const string value, const name defaultValue)\ 
    {\ 
     if(name##FromStringMap.count(value) == 0)\ 
     {\ 
      return defaultValue;\ 
     }\ 
     return name##FromStringMap[value];\ 
    }; 

Cách sử dụng:

ENUM(MyEnum, Value1, Value2) 

void main() 
{ 
    string valueName = MyEnumToString(MyEnum::Value2); 
    MyEnum value = MyEnumFromString(valueName, MyEnum::Value1); 
} 

Tôi không phải là C++ chuyên gia, vì vậy hãy cho tôi biết bạn nghĩ gì hay làm thế nào để làm tốt hơn.

Các vấn đề liên quan