2011-02-11 32 views
36

Tôi đang đọc giá trị enum từ tệp nhị phân và muốn kiểm tra xem giá trị có thực sự là một phần của giá trị enum hay không. Tôi làm nó như thế nào?Cách kiểm tra xem giá trị enum có hợp lệ không?

#include <iostream> 

enum Abc 
{ 
    A = 4, 
    B = 8, 
    C = 12 
}; 

int main() 
{ 
    int v1 = 4; 
    Abc v2 = static_cast<Abc>(v1); 

    switch (v2) 
    { 
     case A: 
      std::cout<<"A"<<std::endl; 
      break; 
     case B: 
      std::cout<<"B"<<std::endl; 
      break; 
     case C: 
      std::cout<<"C"<<std::endl; 
      break; 
     default : 
      std::cout<<"no match found"<<std::endl; 
    } 
} 

Tôi có phải sử dụng toán tử switch hoặc có cách nào tốt hơn không?

EDIT

tôi có giá trị enum thiết lập và tiếc là tôi không thể thay đổi chúng. Để làm cho mọi việc tồi tệ hơn, họ không liên tục (giá trị của họ đi 0, 75,76,80,85,90,95,100, vv)

+3

Bất kỳ enum chỉ là một số, vì vậy tôi không nghĩ có cách nào tốt hơn để kiểm tra nó. Bạn có lẽ nên xác định một cấu trúc cứng nhắc hơn cho các kiểu dữ liệu của bạn. – Rizo

Trả lời

21
Giá trị

enum hợp lệ trong C++ nếu nó nằm trong phạm vi [A, B], được xác định theo quy tắc chuẩn bên dưới. Vì vậy, trong trường hợp của enum X { A = 1, B = 3 }, giá trị của 2 được coi là giá trị enum hợp lệ.

Xem xét 7.2/6 tiêu chuẩn:

Đối với một điều tra nơi EMIN là điều tra viên nhỏ nhất và Emax là lớn nhất, các giá trị thuộc kiểu liệt kê là những giá trị của các loại cơ bản trong bmin phạm vi để Bmax, trong đó bmin và bmax tương ứng là các giá trị nhỏ nhất và lớn nhất của trường bit nhỏ nhất có thể lưu trữ emin và emax. Có thể xác định một điều tra có giá trị không được xác định bởi bất kỳ điều tra viên nào của nó.

Không có hồi cứu trong C++. Một cách tiếp cận để thực hiện là liệt kê các giá trị enum trong một mảng bổ sung và viết một trình bao bọc có thể thực hiện chuyển đổi và có thể ném một ngoại lệ về lỗi.

Xem Similar Question về cách truyền int vào enum để biết thêm chi tiết.

+1

bạn đã hiểu sai câu trích dẫn chuẩn, có nhiều hơn '[A, B]' trong các giá trị hợp lệ. –

+5

Thật vậy, ví dụ: nếu các giá trị là 1 và 5, thì giá trị sau yêu cầu ít nhất 3 bit, vì vậy 6 và 7 cũng sẽ là các giá trị hợp lệ của điều tra viên. – visitor

+0

@Matthieu: Cảm ơn, đã chỉnh sửa câu trả lời. – Leonid

9

Có lẽ sử dụng enum như thế này:

enum MyEnum 
{ 
A, 
B, 
C 
}; 

và kiểm tra

if (v2 >= A && v2 <= C) 

Nếu bạn không chỉ định giá trị cho hằng số enum, giá trị bắt đầu bằng 0 và tăng giá trị bằng mỗi lần di chuyển xuống danh sách. Ví dụ, cho enum MyEnumType { ALPHA, BETA, GAMMA }; ALPHA có giá trị từ 0, BETA có giá trị là 1, và GAMMA có giá trị 2.

+1

Tôi thích sự đơn giản của điều này và mở rộng nó bằng cách luôn luôn xác định mục đầu tiên trong một enum như SOMETYPE_UNKNOWN và cái cuối cùng là SOMETYPE_MAX. Sau đó, kiểm tra sẽ luôn là AssertTrue (v2> = SOMETYPE_UNKNOWN && v2 <= SOMETYPE_MAX). Tất nhiên, chỉ bao giờ thêm các mục sau UNKNOWN và trước MAX. –

+0

Tôi không hiểu ý tưởng của bạn lúc đầu, nhưng nó thực sự là một thủ thuật tuyệt vời, không bảo trì! – pfabri

2

Phát biểu về một ngôn ngữ, không có cách nào tốt hơn, các giá trị enum tồn tại thời gian biên dịch chỉ và không có cách nào để liệt kê chúng một cách có lập trình. Tuy nhiên, với cơ sở hạ tầng có ý tưởng tốt, bạn vẫn có thể tránh liệt kê tất cả các giá trị nhiều lần. Xem Easy way to use variables of enum types as string in C?

mẫu của bạn sau đó có thể được viết lại bằng cách sử dụng "enumFactory.h" cung cấp đó như:

#include "enumFactory.h" 

#define ABC_ENUM(XX) \ 
    XX(A,=4) \ 
    XX(B,=8) \ 
    XX(C,=12) \ 

DECLARE_ENUM(Abc,ABC_ENUM) 

int main() 
{ 
    int v1 = 4; 
    Abc v2 = static_cast<Abc>(v1); 

    #define CHECK_ENUM_CASE(name,assign) case name: std::cout<< #name <<std::endl; break; 
    switch (v2) 
    { 
     ABC_ENUM(CHECK_ENUM_CASE) 
     default : 
      std::cout<<"no match found"<<std::endl; 
    } 
    #undef CHECK_ENUM_CASE 
} 

hoặc thậm chí (sử dụng một số các tiện nghi khác đã tồn tại trong tiêu đề đó):

#include "enumFactory.h" 

#define ABC_ENUM(XX) \ 
    XX(A,=4) \ 
    XX(B,=8) \ 
    XX(C,=12) \ 

DECLARE_ENUM(Abc,ABC_ENUM) 
DEFINE_ENUM(Abc,ABC_ENUM) 

int main() 
{ 
    int v1 = 4; 
    Abc v2 = static_cast<Abc>(v1); 
    const char *name = GetString(v2); 
    if (name[0]==0) name = "no match found"; 
    std::cout << name << std::endl; 
} 
7

Cách duy nhất tôi từng tìm thấy để làm cho nó 'dễ dàng', là tạo ra (vĩ mô) một mảng sắp xếp của các enums và kiểm tra với điều đó.

lừa switch không thành công với enum s vì một số enum có thể có nhiều điều tra viên có giá trị nhất định.

Thực sự là một vấn đề khó chịu.

3

Extensions quản lý cho C++ hỗ trợ cú pháp sau:

enum Abc 
{ 
    A = 4, 
    B = 8, 
    C = 12 
}; 

Enum::IsDefined(Abc::typeid, 8); 

tham khảo: MSDN "Managed Extensions for C++ Programming"

+0

Tôi không chắc chắn những gì "quản lý c + +" là, nhưng bạn có chắc chắn nó là c + +, và không C#? [this] (http://msdn.microsoft.com/en-us/library/system.enum.isdefined%28v=vs.110%29.aspx) trông giống như C# –

+3

@ BЈовић: 'managed C++' là một biến thể Microsoft của 'C++' có thể sử dụng các thư viện của' .NET framework'. Có vẻ như đây là 'C++' vì toán tử' :: 'không được định nghĩa trong' C# 'như thế này. – Stefan

+0

@ BЈовић bạn đã thử mã trong dự án C++ mở rộng được quản lý chưa? Chúng tôi đang sử dụng mã tương tự trong một trong các dự án C++ của chúng tôi. Cụ thể là chúng ta sử dụng phương thức Enum :: IsDefined(). – Brett

4

Trong C++ 11 có một cách tốt hơn nếu bạn đang chuẩn bị để liệt kê các giá trị enum của bạn như thông số mẫu . Bạn có thể xem đây là một điều tốt, cho phép bạn chấp nhận tập con của các giá trị enum hợp lệ trong các ngữ cảnh khác nhau; thường hữu ích khi phân tích mã từ các nguồn bên ngoài.

Một bổ sung hữu ích có thể có vào ví dụ dưới đây sẽ là một số xác nhận tĩnh xung quanh loại cơ bản của EnumType liên quan đến IntType để tránh các vấn đề cắt ngắn. Còn lại như một bài tập.

#include <stdio.h> 

template<typename EnumType, EnumType... Values> class EnumCheck; 

template<typename EnumType> class EnumCheck<EnumType> 
{ 
public: 
    template<typename IntType> 
    static bool constexpr is_value(IntType) { return false; } 
}; 

template<typename EnumType, EnumType V, EnumType... Next> 
class EnumCheck<EnumType, V, Next...> : private EnumCheck<EnumType, Next...> 
{ 
    using super = EnumCheck<EnumType, Next...>; 

public: 
    template<typename IntType> 
    static bool constexpr is_value(IntType v) 
    { 
     return v == static_cast<IntType>(V) || super::is_value(v); 
    } 
}; 

enum class Test { 
    A = 1, 
    C = 3, 
    E = 5 
}; 

using TestCheck = EnumCheck<Test, Test::A, Test::C, Test::E>; 

void check_value(int v) 
{ 
    if (TestCheck::is_value(v)) 
     printf("%d is OK\n", v); 
    else 
     printf("%d is not OK\n", v); 
} 

int main() 
{ 
    for (int i = 0; i < 10; ++i) 
     check_value(i); 
} 
+0

Vì giá trị của 'int v' không được biết tại thời gian biên dịch, 'is_value' sẽ phải được thực thi tại thời gian chạy. Điều này sẽ không dẫn đến tất cả các loại gọi hàm đệ qui và rất kém hiệu quả so với câu lệnh chuyển đổi đơn giản hay một mảng của tất cả các giá trị? Bạn vẫn phải liệt kê tất cả các giá trị enum, do đó, nó không giống như bạn đang đạt được bất cứ điều gì với cách tiếp cận này. Hay tôi đã bỏ lỡ điều gì đó ở đây? –

+0

@InnocentBystander Chúng đều là các hàm 'constexpr', do đó trình biên dịch có rất nhiều phạm vi để tối ưu hóa. Các hàm cũng không đệ quy; đó là một chuỗi các hàm có cùng tên. Trong một số thử nghiệm nhanh với ví dụ trên, gcc 5.4 tạo mã lệnh ngắn hơn cho phiên bản mẫu so với phiên bản chuyển đổi. Clang 3.8 là hai hướng dẫn dài hơn cho phiên bản mẫu. Kết quả sẽ thay đổi tùy thuộc vào số lượng giá trị và các giá trị có tiếp giáp không. Chiến thắng lớn, đặc biệt là khi thực hiện giải mã giao thức, là bạn viết mã bạn mong đợi trên một dòng. – janm

+1

bạn nói đúng - xin lỗi không phải "đệ quy" cho mỗi người, mà là chuỗi các cuộc gọi hàm. Điều thú vị là các trình biên dịch có thể tối ưu hóa tất cả những thứ đó. Và cảm ơn bạn đã theo dõi câu trả lời 3 năm cũ :) –

2

Kinda hoại, nhưng ... làm cho một tấm séc RANGE trong int vào giá trị enum đầu tiên/cuối cùng (có thể được kết hợp với ý tưởng janm để làm kiểm tra chính xác), C++ 11:

Tiêu đề: khai

namespace chkenum 
{ 
    template <class T, T begin, T end> 
    struct RangeCheck 
    { 
    private: 
     typedef typename std::underlying_type<T>::type val_t; 
    public: 
     static 
     typename std::enable_if<std::is_enum<T>::value, bool>::type 
     inrange(val_t value) 
     { 
      return value >= static_cast<val_t>(begin) && value <= static_cast<val_t>(end); 
     } 
    }; 

    template<class T> 
    struct EnumCheck; 
} 

#define DECLARE_ENUM_CHECK(T,B,E) namespace chkenum {template<> struct EnumCheck<T> : public RangeCheck<T, B, E> {};} 

template<class T> 
inline 
typename std::enable_if<std::is_enum<T>::value, bool>::type 
testEnumRange(int val) 
{ 
    return chkenum::EnumCheck<T>::inrange(val); 
} 

Enum:

enum MinMaxType 
{ 
    Max = 0x800, Min, Equal 
}; 
DECLARE_ENUM_CHECK(MinMaxType, MinMaxType::Max, MinMaxType::Equal); 

Cách sử dụng:

bool r = testEnumRange<MinMaxType>(i); 

Chủ yếu là sự khác biệt ở trên đề xuất rằng chức năng kiểm tra chỉ phụ thuộc vào loại enum.

0

Tuy nhiên, một cách khác để làm điều đó:

#include <algorithm> 
#include <iterator> 
#include <iostream> 

template<typename> 
struct enum_traits { static constexpr void* values = nullptr; }; 

namespace detail 
{ 

template<typename T> 
constexpr bool is_value_of(int, void*) { return false; } 

template<typename T, typename U> 
constexpr bool is_value_of(int v, U) 
{ 
    using std::begin; using std::end; 

    return std::find_if(begin(enum_traits<T>::values), end(enum_traits<T>::values), 
     [=](auto value){ return value == static_cast<T>(v); } 
    ) != end(enum_traits<T>::values); 
} 

} 

template<typename T> 
constexpr bool is_value_of(int v) 
{ return detail::is_value_of<T>(v, decltype(enum_traits<T>::values) { }); } 

//////////////////// 
enum Abc { A = 4, B = 8, C = 12 }; 

template<> 
struct enum_traits<Abc> { static constexpr auto values = { A, B, C }; }; 
decltype(enum_traits<Abc>::values) enum_traits<Abc>::values; 

enum class Def { D = 1, E = 3, F = 5 }; 

int main() 
{ 
    std::cout << "Abc:"; 
    for(int i = 0; i < 10; ++i) 
     if(is_value_of<Abc>(i)) std::cout << " " << i; 
    std::cout << std::endl; 

    std::cout << "Def:"; 
    for(int i = 0; i < 10; ++i) 
     if(is_value_of<Def>(i)) std::cout << " " << i; 
    std::cout << std::endl; 

    return 0; 
} 

Các "xấu xí" một phần của cách tiếp cận IMHO này là cần phải xác định:

decltype(enum_traits<Abc>::values) enum_traits<Abc>::values 

Nếu bạn không trái ngược với các macro, bạn có thể quấn nó bên trong một macro:

#define REGISTER_ENUM_VALUES(name, ...) \ 
template<> struct enum_traits<name> { static constexpr auto values = { __VA_ARGS__ }; }; \ 
decltype(enum_traits<name>::values) enum_traits<name>::values; 
Các vấn đề liên quan