C ++ में Enum करने के लिए int कास्ट करने का सामान्य तरीका


82

वहाँ एक सामान्य तरीके कास्ट करने के लिए है intकरने के लिए enumमें C++?

अगर इसकी intसीमा में गिरता है तो enumउसे एक enumमूल्य वापस करना चाहिए , अन्यथा एक फेंक दें exception। क्या इसे उदारतापूर्वक लिखने का कोई तरीका है ? एक से अधिक का enum typeसमर्थन किया जाना चाहिए।

पृष्ठभूमि: मेरे पास एक बाहरी ईनम प्रकार है और स्रोत कोड पर कोई नियंत्रण नहीं है । मैं एक डेटाबेस में इस मूल्य को संग्रहीत करना चाहता हूं और इसे पुनः प्राप्त करता हूं।


enum e{x = 10000};इस मामले में 9999किसकी सीमा में आता है enum?
आर्मेन त्सिर्युन्यान

नहीं, 9999गिरता नहीं है।
लियोनिद

9
अच्छा प्रश्न। किसी भी "क्यों?" जो प्रकट होने वाला है, मुझे केवल "डिसेरिएलाइज़ेशन" कहना है - मेरे लिए पर्याप्त कारण लगता है। मुझे भी C ++ 0x-compilant उत्तर के लिए सुनकर खुशी होगी enum class
कोस

9
"श्रेणी" यहाँ गलत शब्द है, शायद "डोमेन"?
कॉन्स्टेंटिन

बढ़ावा :: num_cast <> एक सकारात्मक या नकारात्मक अतिप्रवाह अपवाद फेंकता है अगर सीमा से बाहर मूल्य। लेकिन यह सुनिश्चित नहीं है कि यह एनम प्रकारों के लिए भी अच्छा है या नहीं। आप कोशिश कर सकते हैं कि
यासूसर

जवाबों:


38

स्पष्ट बात यह है कि आपके एनम को एनोटेट करना है:

// generic code
#include <algorithm>

template <typename T>
struct enum_traits {};

template<typename T, size_t N>
T *endof(T (&ra)[N]) {
    return ra + N;
}

template<typename T, typename ValType>
T check(ValType v) {
    typedef enum_traits<T> traits;
    const T *first = traits::enumerators;
    const T *last = endof(traits::enumerators);
    if (traits::sorted) { // probably premature optimization
        if (std::binary_search(first, last, v)) return T(v);
    } else if (std::find(first, last, v) != last) {
        return T(v);
    }
    throw "exception";
}

// "enhanced" definition of enum
enum e {
    x = 1,
    y = 4,
    z = 10,
};

template<>
struct enum_traits<e> {
    static const e enumerators[];
    static const bool sorted = true;
};
// must appear in only one TU,
// so if the above is in a header then it will need the array size
const e enum_traits<e>::enumerators[] = {x, y, z};

// usage
int main() {
    e good = check<e>(1);
    e bad = check<e>(2);
}

eयदि आप लेखक नहीं हैं, तो आपको एक तिथि के साथ रखने की आवश्यकता है , जो एक उपद्रव है e। जैसा कि सोज़र्ड कहते हैं, यह संभवतः किसी भी सभ्य निर्माण प्रणाली के साथ स्वचालित हो सकता है।

किसी भी मामले में, आप 7.2 / 6 के खिलाफ हैं:

एक गणना के लिए जहां एमिन सबसे छोटा एन्यूमरेटर होता है और एमैक्स सबसे बड़ा होता है, एन्यूमरेशन के मान रेंज में bmax में अंतर्निहित प्रकार के मान होते हैं, जहां bmin और bmax क्रमशः सबसे छोटे और सबसे छोटे मान होते हैं बिट-फील्ड जो एमिन और इमैक्स को स्टोर कर सकता है। एक गणना को परिभाषित करना संभव है, जिसमें इसके किसी भी एन्यूमरेटर द्वारा परिभाषित मूल्य नहीं हैं।

इसलिए यदि आप इसके लेखक नहीं हैं, तो आपको इस बात की eगारंटी हो सकती है कि eवास्तव में इसकी परिभाषा में मान्य मूल्य दिखाई देते हैं या नहीं।


22

बदसूरत।

enum MyEnum { one = 1, two = 2 };

MyEnum to_enum(int n)
{
  switch( n )
  {
    case 1 :  return one;
    case 2 : return two;
  }
  throw something();
}

अब असली सवाल के लिए। आप इसकी आवश्यकता क्यों है? कोड बदसूरत है, लिखना आसान नहीं है (*?) और बनाए रखना आसान नहीं है, और अपने कोड में शामिल करना आसान नहीं है। यह कोड आपको बता रहा है कि यह गलत है। इससे क्यों लड़ें?

संपादित करें:

वैकल्पिक रूप से, यह देखते हुए कि एन + सी ++ में अभिन्न प्रकार हैं:

enum my_enum_val = static_cast<MyEnum>(my_int_val);

लेकिन यह और भी बदसूरत है कि ऊपर, त्रुटियों के लिए बहुत अधिक प्रवण, और यह आप की इच्छा के रूप में फेंक नहीं होगा।


यह केवल एक प्रकार का समर्थन करता है, MyEnum।
सिमोन

2
@ लियोनिड: मेरे ज्ञान के लिए यह उदारतापूर्वक नहीं किया जा सकता है। किसी स्तर पर, throwअमान्य प्रकारों के लिए आप जो भी समाधान (या कुछ विशेष करेंगे) लेकर आए हैं , मेरे पास एक स्विच होना चाहिए जैसे मैंने पोस्ट किया है।
जॉन डिबलिंग

2
ये -1’एड क्यों है? इसका सही उत्तर है। सिर्फ इसलिए कि यह जवाब नहीं है कुछ के लिए उम्मीद कर रहे थे इसका मतलब यह नहीं है कि यह गलत है।
जॉन डिबलिंग

12
A static_cast<MyEnum>भी काम करेगा, और reinterpret_cast<MyEnum>
Sjoerd

1
यह दृष्टिकोण अच्छी तरह से काम करता है, और इससे भी बेहतर है यदि आप फ़ंक्शन को उत्पन्न करने के लिए एक उपकरण का उपयोग करते हैं।
निक

3

यदि, जैसा कि आप वर्णन करते हैं, मान एक डेटाबेस में हैं, तो एक कोड जनरेटर क्यों नहीं लिखा जाता है जो इस तालिका को पढ़ता है और एनम और to_enum(int)फ़ंक्शन दोनों के साथ .h और .cpp फ़ाइल बनाता है ?

लाभ:

  • एक to_string(my_enum)फ़ंक्शन को जोड़ना आसान है ।
  • थोड़ा रखरखाव की आवश्यकता है
  • डेटाबेस और कोड सिंक में हैं

Enum type source code पर कोई नियंत्रण नहीं है । मैं मानता हूं कि एक ऐसी सुविधा उत्पन्न की जा सकती है जो रूपांतरण को लागू करती है। हालांकि, सुविधा बाहरी एनुम प्रकार (जब तक कि संकलन समय पर हर बार निष्पादित नहीं होती है) में किसी भी बदलाव / विस्तार के बारे में पता नहीं होगा ।
लियोनिद

@ लियोनिड तब उस एनम हेडर को पढ़ें और उसके to_enum(int)आधार पर फ़ंक्शन उत्पन्न करें ।
मोजरड

@ लियोनिड हर गंभीर परियोजना प्रबंधन प्रणाली, यहां makeतक कि दो फाइलों की तारीख की तुलना यह देखने के लिए कर सकती है कि जनरेटर को फिर से चालू करना है या नहीं।
Sjoerd

मुझे लगता है कि मैं अब के लिए जनरेटर के लिए एक सरल समाधान के साथ जाऊंगा। लेकिन विचार के लिए धन्यवाद।
लियोनिड

हम अपने कार्यस्थल पर इस योजना का उपयोग करते हैं, एक टूल टेम्पलेट से .hpp कोड उत्पन्न करता है, टेम्पलेट न्यूनतम होता है।
निक

3

नहीं- C ++ में कोई आत्मनिरीक्षण नहीं है, और न ही "डोमेन चेक" सुविधा में कोई बनाया गया है।


2

आप इस बारे में क्या सोचते हैं?

#include <iostream>
#include <stdexcept>
#include <set>
#include <string>

using namespace std;

template<typename T>
class Enum
{
public:
    static void insert(int value)
    {
        _set.insert(value);
    }

    static T buildFrom(int value)
    {
        if (_set.find(value) != _set.end()) {
            T retval;
            retval.assign(value);
            return retval;
        }
        throw std::runtime_error("unexpected value");
    }

    operator int() const { return _value; }

private:
    void assign(int value)
    {
        _value = value;
    }

    int _value;
    static std::set<int> _set;
};

template<typename T> std::set<int> Enum<T>::_set;

class Apples: public Enum<Apples> {};

class Oranges: public Enum<Oranges> {};

class Proxy
{
public:
    Proxy(int value): _value(value) {}

    template<typename T>
    operator T()
    {
        T theEnum;
        return theEnum.buildFrom(_value);
    }

    int _value;
};

Proxy convert(int value)
{
    return Proxy(value);
}

int main()
{    
    Apples::insert(4);
    Apples::insert(8);

    Apples a = convert(4); // works
    std::cout << a << std::endl; // prints 4

    try {
        Apples b = convert(9); // throws    
    }
    catch (std::exception const& e) {
        std::cout << e.what() << std::endl; // prints "unexpected value"
    }
    try {
        Oranges b = convert(4); // also throws  
    }
    catch (std::exception const& e) {
        std::cout << e.what() << std::endl; // prints "unexpected value"
    }
}

फिर आप मूल्यों पर स्विच करने के लिए यहां पोस्ट कोड का उपयोग कर सकते हैं।


आपको अभी भी Apples::insert(4)कहीं न कहीं जोड़ने की आवश्यकता है, इसलिए स्विच पर इसका कोई लाभ नहीं है।
सोजेरड

1
उन्होंने कहा कि मान एक डेटाबेस से आते हैं, इसीलिए मैंने एक "इन्सर्ट" विधि जोड़ी है।
सिमोन

1

आपको ऐसा कुछ नहीं चाहिए जो आप मौजूद होने का वर्णन करते हैं, मुझे डर है कि आपके कोड डिजाइन में समस्याएं हैं।

इसके अलावा, आप मानते हैं कि एनम एक सीमा में आते हैं, लेकिन हमेशा ऐसा नहीं होता है:

enum Flags { one = 1, two = 2, four = 4, eigh = 8, big = 2000000000 };

यह एक सीमा में नहीं है: भले ही यह संभव था, क्या आप 0 से 2 ^ n तक हर पूर्णांक की जांच करने वाले हैं, यह देखने के लिए कि क्या वे कुछ एनम के मूल्य से मेल खाते हैं?


अन्यथा आप डेटाबेस से एनम मूल्यों को कैसे प्राप्त करेंगे? पूर्णांकों को संकलन समय पर जाना जाता है, इसलिए टेम्प्लेट के आधार पर सामान्य रूपांतरण करना संभव क्यों नहीं है?
लियोनिद

2
@ लियोनिड: क्योंकि कुछ स्तर पर, आपको एक स्विच करने की आवश्यकता है, जैसे मैंने कहा।
जॉन डिबलिंग

2
@ लेओनिड टेम्प्लेट एक चांदी की गोली नहीं है जिसे आप हर समस्या को हल कर सकते हैं।
१२:२२

जॉन सही है। आपको एनम की तुलना में एक प्रकार का अधिक जटिल चाहिए जो आप चाहते हैं, मुझे लगता है कि यह एक वर्ग पदानुक्रम के साथ संभव है।
सिमोन

मैंने एक समाधान पोस्ट किया जो वर्ग पदानुक्रम का उपयोग करता है, इसे देखें।
सिमोन

1

यदि आप अपने एनम वैल्यूज़ को टेम्प्लेट पैरामीटर के रूप में सूचीबद्ध करने के लिए तैयार हैं, तो आप इसे वैरिएबल टेम्प्लेट के साथ C ++ 11 में कर सकते हैं। आप इसे एक अच्छी बात के रूप में देख सकते हैं, जिससे आप विभिन्न संदर्भों में मान्य एनम मूल्यों के सबसेट को स्वीकार कर सकते हैं; बाहरी स्रोतों से कोड पार्स करते समय अक्सर उपयोगी होता है।

शायद उतना सामान्य नहीं है जितना आप चाहते हैं, लेकिन चेकिंग कोड सामान्यीकृत है, आपको केवल मूल्यों के सेट को निर्दिष्ट करने की आवश्यकता है। यह दृष्टिकोण अंतराल, मनमानी मूल्यों आदि को संभालता है।

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<typename std::underlying_type<EnumType>::type>(V) || super::is_value(v);
    }

    EnumType convert(IntType v)
    {
        if (!is_value(v)) throw std::runtime_error("Enum value out of range");
        return static_cast<EnumType>(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);
}

हालांकि यह लिंक प्रश्न का उत्तर दे सकता है, लेकिन उत्तर के आवश्यक हिस्सों को यहां शामिल करना और संदर्भ के लिए लिंक प्रदान करना बेहतर है। लिंक-केवल उत्तर अमान्य हो सकते हैं यदि लिंक किए गए पृष्ठ बदल जाते हैं। - समीक्षा से
तस

1
@ यह एक अलग एसओ उत्तर के लिए एक कड़ी है - इसमें वही मुद्दे नहीं हैं जो एक बाहरी लिंक है। वैसे भी अपडेट किया गया।
Janm

0

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

#include <cassert>  // assert

namespace  // unnamed namespace
{
    enum class e1 { value_1 = 1, value_2 = 2 };
    enum class e2 { value_3 = 3, value_4 = 4 };

    template <typename T>
    int valid_enum( const int val, const T& vec )
    {
        for ( const auto item : vec )
            if ( static_cast<int>( item ) == val ) return val;

        throw std::exception( "invalid enum value!" );  // throw something useful here
    }   // valid_enum
}   // ns

int main()
{
    // generate list of valid values
    const auto e1_valid_values = { e1::value_1, e1::value_2 };
    const auto e2_valid_values = { e2::value_3, e2::value_4 };

    auto result1 = static_cast<e1>( valid_enum( 1, e1_valid_values ) );
    assert( result1 == e1::value_1 );

    auto result2 = static_cast<e2>( valid_enum( 3, e2_valid_values ) );
    assert( result2 == e2::value_3 );

    // test throw on invalid value
    try
    {
        auto result3 = static_cast<e1>( valid_enum( 9999999, e1_valid_values ) );
        assert( false );
    }
    catch ( ... )
    {
        assert( true );
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.