2009-09-07 7 views
37

सी ++ में एक enum पर गणना करें, क्या यह enum (या तो रनटाइम या संकलन समय (पसंदीदा)) पर गणना करना संभव है और प्रत्येक पुनरावृत्ति के लिए फ़ंक्शन/जेनरेट कोड कॉल करना संभव है?सी ++

नमूना उपयोग के मामले:

enum abc 
{  
    start 
    a, 
    b, 
    c, 
    end 
}  
for each (__enum__member__ in abc) 
{  
    function_call(__enum__member__);  
} 

प्रशंसनीय डुप्लिकेट:

+0

आप कॉल के लिए फ़ंक्शन चुनने का अनुमान कैसे लगाते हैं? क्या आप कुछ छद्म कोड पोस्ट कर सकते हैं आप इसे कैसे मानते हैं? यह आपकी मदद करने में हमारी मदद कर सकता है। –

+0

क्रिल – jameszhao00

+0

के लिए अपडेट किया गया रनटाइम के लिए http://stackoverflow.com/questions/1292426/is-there-any-well-known-paradigm-for-iterating-through-enum पर देखें। (यदि यह संकलन-समय के लिए नहीं था, तो आपका प्रश्न इसका सटीक डुप्लिकेट होगा।) – sbi

उत्तर

54

को जोड़ने के लिए जवाब @StackedCrooked के लिए, आप operator++, operator-- और operator* ओवरलोड और कार्यक्षमता की तरह इटरेटर हो सकता है। कुछ <algorithm> टेम्पलेट

void print(Color c) { 
    std::cout << c << std::endl; 
} 

int main() { 
    std::for_each(Color_Begin, Color_End, &print); 
} 

साथ

enum Color { 
    Color_Begin, 
    Color_Red = Color_Begin, 
    Color_Orange, 
    Color_Yellow, 
    Color_Green, 
    Color_Blue, 
    Color_Indigo, 
    Color_Violet, 
    Color_End 
}; 

namespace std { 
template<> 
struct iterator_traits<Color> { 
    typedef Color value_type; 
    typedef int difference_type; 
    typedef Color *pointer; 
    typedef Color &reference; 
    typedef std::bidirectional_iterator_tag 
    iterator_category; 
}; 
} 

Color &operator++(Color &c) { 
    assert(c != Color_End); 
    c = static_cast<Color>(c + 1); 
    return c; 
} 

Color operator++(Color &c, int) { 
    assert(c != Color_End); 
    ++c; 
    return static_cast<Color>(c - 1); 
} 

Color &operator--(Color &c) { 
    assert(c != Color_Begin); 
    return c = static_cast<Color>(c - 1); 
} 

Color operator--(Color &c, int) { 
    assert(c != Color_Begin); 
    --c; 
    return static_cast<Color>(c + 1); 
} 

Color operator*(Color c) { 
    assert(c != Color_End); 
    return c; 
} 

आइए परीक्षण अब, Color एक निरंतर द्विदिश इटरेटर है। यहां एक पुन: प्रयोज्य वर्ग है जिसे मैंने मैन्युअल रूप से ऊपर करते हुए कोड किया था। मैं इसे, कई और अधिक enums के लिए काम कर सकता था तो एक ही कोड दोहरा देखा फिर से काफी थकाऊ

// Code for testing enum_iterator 
// -------------------------------- 

namespace color_test { 
enum Color { 
    Color_Begin, 
    Color_Red = Color_Begin, 
    Color_Orange, 
    Color_Yellow, 
    Color_Green, 
    Color_Blue, 
    Color_Indigo, 
    Color_Violet, 
    Color_End 
}; 

Color begin(enum_identity<Color>) { 
    return Color_Begin; 
} 

Color end(enum_identity<Color>) { 
    return Color_End; 
} 
} 

void print(color_test::Color c) { 
    std::cout << c << std::endl; 
} 

int main() { 
    enum_iterator<color_test::Color> b = color_test::Color_Begin, e; 
    while(b != e) 
    print(*b++); 
} 

कार्यान्वयन इस प्रकार है।

template<typename T> 
struct enum_identity { 
    typedef T type; 
}; 

namespace details { 
void begin(); 
void end(); 
} 

template<typename Enum> 
struct enum_iterator 
    : std::iterator<std::bidirectional_iterator_tag, 
        Enum> { 
    enum_iterator():c(end()) { } 

    enum_iterator(Enum c):c(c) { 
    assert(c >= begin() && c <= end()); 
    } 

    enum_iterator &operator=(Enum c) { 
    assert(c >= begin() && c <= end()); 
    this->c = c; 
    return *this; 
    } 

    static Enum begin() { 
    using details::begin; // re-enable ADL 
    return begin(enum_identity<Enum>()); 
    } 

    static Enum end() { 
    using details::end; // re-enable ADL 
    return end(enum_identity<Enum>()); 
    } 

    enum_iterator &operator++() { 
    assert(c != end() && "incrementing past end?"); 
    c = static_cast<Enum>(c + 1); 
    return *this; 
    } 

    enum_iterator operator++(int) { 
    assert(c != end() && "incrementing past end?"); 
    enum_iterator cpy(*this); 
    ++*this; 
    return cpy; 
    } 

    enum_iterator &operator--() { 
    assert(c != begin() && "decrementing beyond begin?"); 
    c = static_cast<Enum>(c - 1); 
    return *this; 
    } 

    enum_iterator operator--(int) { 
    assert(c != begin() && "decrementing beyond begin?"); 
    enum_iterator cpy(*this); 
    --*this; 
    return cpy; 
    } 

    Enum operator*() { 
    assert(c != end() && "cannot dereference end iterator"); 
    return c; 
    } 

    Enum get_enum() const { 
    return c; 
    } 

private: 
    Enum c; 
}; 

template<typename Enum> 
bool operator==(enum_iterator<Enum> e1, enum_iterator<Enum> e2) { 
    return e1.get_enum() == e2.get_enum(); 
} 

template<typename Enum> 
bool operator!=(enum_iterator<Enum> e1, enum_iterator<Enum> e2) { 
    return !(e1 == e2); 
} 
+3

+1: यह वास्तव में सुरुचिपूर्ण है। – ereOn

4

न तो एक littl के बिना संभव है ई मैनुअल श्रम। मैक्रोज़ द्वारा बहुत सारे काम किए जा सकते हैं, यदि आप उस क्षेत्र में जाने के इच्छुक हैं।

1

नहीं

हालांकि, आप अपने खुद के वर्ग कि पुनरावृत्तियों साथ enum जैसी सुविधाओं को लागू करता है निर्धारित कर सकते हैं। आप प्री 1.5 जावा दिनों से एक चाल को याद कर सकते हैं, जिसे "टाइप एनम डिज़ाइन पैटर्न" कहा जाता है। आप सी ++ समकक्ष कर सकते हैं।

mystuff.h:

#ifndef LAST_ENUM_ELEMENT 
#define LAST_ENUM_ELEMENT(ARG) ENUM_ELEMENT(ARG) 
#endif 

ENUM_ELEMENT(foo) 
ENUM_ELEMENT(bar) 
LAST_ENUM_ELEMENT(baz) 

// not essential, but most likely every "caller" should do it anyway... 
#undef LAST_ENUM_ELEMENT 
#undef ENUM_ELEMENT 

2

क्या कोनराड कहते विस्तार करते हुए, "प्रत्येक यात्रा के लिए कोड उत्पन्न" के मामले में एक संभव मुहावरा एक शामिल फ़ाइल का उपयोग करने गणन प्रतिनिधित्व करने के लिए है enum.h:

// include guard goes here (but mystuff.h doesn't have one) 

enum element { 
    #define ENUM_ELEMENT(ARG) ARG, 
    #define LAST_ENUM_ELEMENT(ARG) ARG 
    #include "mystuff.h" 
} 

main.cpp:

#include "enum.h" 
#define ENUM_ELEMENT(ARG) void do_##ARG(); 
#include "mystuff.h" 

element value = getValue(); 
switch(value) { 
    #define ENUM_ELEMENT(ARG) case ARG: do_##ARG(); break; 
    #include "mystuff.h" 
    default: std::terminate(); 
} 

तो, एक नया तत्व "qux" जोड़ने के लिए, आप इसे mystuff.h में जोड़ते हैं और do_qux फ़ंक्शन लिखते हैं। आपको प्रेषण कोड को छूने की ज़रूरत नहीं है।

बेशक यदि आपके enum में मानों को निरंतर निरंतर पूर्णांक होने की आवश्यकता है, तो आप enum परिभाषा को बनाए रखने और ENUM_ELEMENT(foo) ... अलग से सूची, जो गन्दा है।

1

यह मेरे लिए hacky लगता है, लेकिन अपने उद्देश्यों के अनुरूप हो सकता है:

enum Blah { 
    FOO, 
    BAR, 
    NUM_BLAHS 
}; 

// later on 
for (int i = 0; i < NUM_BLAHS; ++i) { 
    switch (i) { 
    case FOO: 
    // foo stuff 
    break; 
    case BAR: 
    // bar stuff 
    break; 
    default: 
    // you're missing a case statement 
    } 
} 

आप एक विशेष शुरू मूल्य की जरूरत है, तो आप उस एक निरंतर बनाने के लिए और अपने enum में सेट कर सकते हैं। मैंने यह जांच नहीं की कि यह संकलित है या नहीं, लेकिन यह वहां होने के करीब होना चाहिए :-)। उम्मीद है की यह मदद करेगा।

मुझे लगता है कि यह दृष्टिकोण आपके उपयोग के मामले के लिए एक अच्छा संतुलन हो सकता है। इसका उपयोग करें यदि आपको विभिन्न गणित प्रकारों के समूह के लिए ऐसा करने की आवश्यकता नहीं है और आप प्रीप्रोसेसर सामग्री से निपटना नहीं चाहते हैं। बस सुनिश्चित करें कि आप टिप्पणी करते हैं और शायद बाद में किसी तारीख को बेहतर तरीके से बदलने के लिए TODO जोड़ते हैं :-)।

+4

'for' और नेस्टेड' स्विच' पूरी तरह से बेकार/अर्थहीन हैं क्योंकि आप लगातार प्रत्येक मूल्य का उपयोग करते हैं। बस दोनों को छोड़ दें और 'foo stuff' निष्पादित करें, इसके बाद 'बार स्टफ' इत्यादि। –

+0

यह केवल एक परीक्षण कार्यक्रम में उपयोगी है जहां आप परीक्षणों की एक श्रृंखला चलाते हैं। प्रारंभ/अंत स्थितियां चर होंगे इसलिए यदि आप केवल एक परीक्षण चलाने के लिए चाहते हैं तो आप प्रारंभ = अंत सेट करें: (int i = start; i <= end; ++ i) {....} अन्य सभी मामलों में, मैं कोनराड से सहमत हूं: एक के अंदर स्विच शायद एक खराब डिजाइन है। – jmucchiello

+2

ए लूप के लिए स्विच स्टेटमेंट के प्रत्येक मामले को हिट करने के लिए? कृप्या। यह stackoverflow है, thedailywtf.com नहीं। यह "फू सामान" करने के लिए एक अनिवार्य रूप से जटिल तरीका है, फिर "बार सामान"। उसमें किसी भी तरह के प्रवाह नियंत्रण की आवश्यकता नहीं है। – Alan

42

सी ++ वर्तमान में गणनाकर्ता पुनरावृत्ति प्रदान नहीं करता है। इसके बावजूद, इसके लिए कभी-कभी आवश्यकता उत्पन्न होती है। एक आम कामकाज उन मूल्यों को जोड़ना है जो शुरुआत और अंत को चिह्नित करते हैं।उदाहरण के लिए:

enum Color 
{ 
    Color_Begin, 
    Color_Red = Color_Begin, 
    Color_Orange, 
    Color_Yellow, 
    Color_Green, 
    Color_Blue, 
    Color_Indigo, 
    Color_Violet, 
    Color_End 
}; 

void foo(Color c) 
{ 
} 


void iterateColors() 
{ 
    for (size_t colorIdx = Color_Begin; colorIdx != Color_End; ++colorIdx) 
    { 
     foo(static_cast<Color>(colorIdx)); 
    } 
} 
0

आप टीएमपी के साथ स्थिर रूप से प्रस्तावित रनटाइम तकनीकों में से कुछ कर सकते हैं।

#include <iostream> 

enum abc 
{ 
    a, 
    b, 
    c, 
    end 
}; 

void function_call(abc val) 
{ 
    std::cout << val << std::endl; 
} 

template<abc val> 
struct iterator_t 
{ 
    static void run() 
    { 
     function_call(val); 

     iterator_t<static_cast<abc>(val + 1)>::run(); 
    } 
}; 

template<> 
struct iterator_t<end> 
{ 
    static void run() 
    { 
    } 
}; 

int main() 
{ 
    iterator_t<a>::run(); 

    return 0; 
} 

इस कार्यक्रम से उत्पादन होता है:

0 
1 
2 

देखें इब्राहीम की Ch 1, Gurtovoy "सी ++ खाका Metaprogramming" इस तकनीक का एक अच्छा इलाज के लिए। लाभ यह प्रस्तावित क्रम तकनीकों पर इस तरह से कर रही है कि, जब आप इस कोड का अनुकूलन, यह स्टैटिक्स इनलाइन सकता है और मोटे तौर पर के बराबर है: संकलक से भी अधिक मदद के लिए

function_call(a); 
function_call(b); 
function_call(c); 

इनलाइन function_call।

अन्य गणित पुनरावृत्ति तकनीकों की एक ही आलोचना यहां लागू होती है। यह तकनीक केवल तभी काम करती है जब आपकी गणना लगातार अंत तक बढ़ती है।

enum abc 
{  
    abc_begin, 
    a = abc_begin, 
    b, 
    c, 
    abc_end 
}; 

void foo() 
{ 
    for(auto&& r : range(abc_begin,abc_end)) 
    { 
     cout << r; 
    } 
} 


range पूरी तरह से सामान्य है, और इस तरह परिभाषित किया गया इस प्रकार है::

1

मैं आमतौर पर है कि इस तरह करते हैं प्यार templating

template <typename T> 
class Range 
{ 
public: 
    Range(const T& beg, const T& end) : b(beg), e(end) {} 
    struct iterator 
    { 
     T val; 
     T operator*() { return val; } 
     iterator& operator++() { val = (T)(1+val); return *this; } 
     bool operator!=(const iterator& i2) { return val != i2.val; } 
    }; 
    iterator begin() const { return{b}; } 
    iterator end() const { return{e}; } 
private: 
    const T& b; 
    const T& e; 
}; 

template <typename T> 
Range<T> range(const T& beg, const T& end) { return Range<T>(beg,end); } 
0

लेकिन मैं के लिए इस पर ध्यान करने के लिए जा रहा हूँ मेरी भविष्य/अन्य लोगों के उपयोग इसलिए हम उपरोक्त में से किसी के साथ खो नहीं गए हैं।

एनम्स एक ज्ञात आदेशित फैशन में चीजों की तुलना करने के लिए सुविधाजनक हैं। उन्हें आम तौर पर पूर्णांक मानों के विरुद्ध पठनीयता के लिए कार्यों में कड़ी-कोडित किया जाता है। कुछ हद तक प्रीप्रोसेसर परिभाषाओं के समान, अपवाद के साथ कि उन्हें अक्षर के साथ प्रतिस्थापित नहीं किया गया है, लेकिन रनटाइम में रखा और एक्सेस किया गया है।

यदि हम परिभाषित करने एचटीएमएल त्रुटि कोड एक enum था और हम जानते थे 500s में कि त्रुटि कोड सर्वर त्रुटियों हैं, यह की तरह कुछ पढ़ने के लिए अच्छे हो सकता है:

enum HtmlCodes {CONTINUE_CODE=100,CLIENT_ERROR=400,SERVER_ERROR=500,NON_STANDARD=600}; 

if(errorCode >= SERVER_ERROR && errorCode < NON_STANDARD) 

से

if(errorCode >= 500 && errorCode < 600) 

मुख्य भाग यह है, वे सरणी के समान हैं! लेकिनकास्टपूर्णांक मान पर उपयोग किया जाता है।

लघु उदाहरण:

enum Suit {Diamonds, Hearts, Clubs, Spades}; 
//does something with values in the enum past "Hearts" in this case 
for(int i=0;i<4;i++){ 
    //Could also use i or Hearts, because the enum will turns these both back into an int 
    if((Suit)(i) > 1) 
    { 
     //Whatever we'd like to do with (Suit)(i) 
    } 
} 

अक्सर enums भी चार के साथ उपयोग किया जाता है * सरणियों या स्ट्रिंग सरणियों ताकि आप जुड़े मूल्य के साथ कुछ संदेश मुद्रित कर सकते हैं। आम तौर पर वे सिर्फ enum में मूल्यों का एक ही सेट के साथ सरणियों रहे हैं, इसलिए जैसे:

char* Suits[4] = {"Diamonds", "Hearts", "Clubs", "Spades"}; 
//Getting a little redundant 
cout << Suits[Clubs] << endl; 
//We might want to add this to the above 
//cout << Suits[(Suit)(i)] << endl; 

और निश्चित रूप से यह एक सामान्य वर्ग जो ऊपर जवाब की तरह enums के लिए यात्रा संभालती बनाने के लिए भी बेहतर होता है।