2015-03-06 17 views
5

मैं टेम्पलेट का उपयोग कर प्रकार सुरक्षित सी ++ झंडे बनाने की कोशिश कर रहा हूं। मैं ध्वज और ध्वज एस (शून्य, एक या कई झंडे होने) के बीच अंतर करना चाहता हूं। नीचेप्रकार के लिए टेम्पलेट सुरक्षित सी ++ 11 एनम वर्ग झंडे

समाधान अच्छा काम करता है, EnumFlag<T> operator | (T, T) किस प्रकार EnumFlag वापस जाने के लिए enums पर सभी | -operations का कारण बनता है के लिए छोड़कर। यह बहुत सारे कोड तोड़ता है। इसे ठीक करने के लिए कोई चाल? मेरे कोड में मैं निम्नलिखित करता हूं, हालांकि हार्ड कोडिंग Option यहां कोई विकल्प नहीं है। यह सामान्य कैसे बनाएं?

EnumFlag<typename std::enable_if<std::is_same<T, Option>::value, T>::type> operator | (T l, T r) 

को यह बदल रहा है ...

EnumFlag<T> operator | (T l, T r) 

... कारण की सब कुछ टूट जाता है। मुझे ऐसा कुछ चाहिए (compilabel कोड नहीं)। या कोई अन्य बेहतर विचार!

EnumFlag<typename std::enable_if<std::already_expanded<EnumFlag<T>>::value, T>::type> operator | (T l, T r) 

पूरा compilable कोड:

EnumFlag.h

#ifndef __classwith_flags_h_ 
#define __classwith_flags_h_ 

#include <type_traits> 

enum class Option 
{ 
    PrintHi = 1 << 0, 
    PrintYo = 1 << 1, 
    PrintAlot = 1 << 2 
}; 

template <typename T> 
class EnumFlag 
{ 
public: 
    using UnderlayingType = typename std::underlying_type<T>::type; 

    EnumFlag(const T& flags) 
     : m_flags(static_cast<UnderlayingType>(flags)) 
    {} 

    bool operator & (T r) const 
    { 
     return 0 != (m_flags & static_cast<UnderlayingType>(r)); 
    } 

    static const T NoFlag = static_cast<T>(0); 

private: 
    UnderlayingType m_flags; 
}; 
template<typename T> 
EnumFlag<typename std::enable_if<std::is_same<T, Option>::value, T>::type> operator | (T l, T r) 
{ 
    return static_cast<T>(static_cast<typename EnumFlag<T>::UnderlayingType>(l) | static_cast<typename EnumFlag<T>::UnderlayingType>(r)); 
} 

class ClassWithFlags 
{ 
public: 
    using Options = EnumFlag <Option>; 

    void doIt(const Options &options); 
}; 

#endif 

EnumFlag.cpp

#include "EnumFlag.h" 

#include <iostream> 

void ClassWithFlags::doIt(const Options &options) 
{ 
    if (options & Option::PrintHi) 
    { 
     std::cout << "Hi" << std::endl; 
    } 
    if (options & Option::PrintYo) 
    { 
     std::cout << "Yo!" << std::endl; 
    } 
} 

int main() 
{ 
    ClassWithFlags classWithFlags; 
    classWithFlags.doIt(Option::PrintHi | Option::PrintAlot); 
} 

>DEMO <

वास्तविक कोड में बहुत अधिक ऑपरेटरों होंगे, हालांकि यह समस्या को स्पष्ट करने के लिए पर्याप्त है।

एक कम दखल समाधान इस (लेकिन अभी भी बहुत दखल)

template<typename T> 
typename std::underlying_type<T>::type operator | (T l, T r) 
{ 
    return (static_cast<typename std::underlying_type<T>::type>(l) | static_cast<typename std::underlying_type<T>::type>(r)); 
} 

नहीं भगवान के लिए पर्याप्त है, तो EnumFlag(const std::underlying_type<T> &flags) मौजूद होना चाहिए और मैं प्रकार सुरक्षा खो देते हैं। साथ ही, मैं वैश्विक ऑपरेटर ओवरलोड को केवल वास्तव में आवश्यक प्रकारों के लिए बनाया जाना चाहता हूं। मैक्रोज़ भी कोई भगवान नहीं है क्योंकि मैं चाहता हूं कि 0 कक्षाओं के अंदर EnumFlag की घोषणा की अनुमति दें। वैश्विक अधिभार वहां नहीं हो सकते हैं, इसलिए मुझे EnumFlag पर बनाने के लिए अलग-अलग स्थानों पर दो मैक्रोज़ कॉल की आवश्यकता है।

समाधान शुद्ध सी ++ 11/एसएलएल होना चाहिए।

+0

'EnumFlag से एक गैर स्पष्ट रूपांतरण जोड़ने नहीं चाहेंगे' टी करने के लिए ' 'EnumFlag ऑपरेटर के कारण होने वाले मुद्दों को ठीक करें। (टी एल, टी आर) '? – Pradhan

+0

मुझे लगता है कि यह एक कामकाज है, जिसे 'एनमफ्लैग' उदाहरण बनाते हैं जब आपको आवश्यकता नहीं होती है, बस वापस परिवर्तित करने के लिए। इसके अलावा 'विकल्प' के उदाहरण के साथ 'foo (const विकल्प & o)' को कॉल करने की अनुमति होगी। इसलिए, प्रकार खोना सुरक्षित। 'Foo' के अंदर हम सभी मामलों को एक हैंडलिंग कर सकते हैं, फिर भी कुछ नहीं होगा ... – Mathias

+0

अपने झंडे को पकड़ने के लिए 'std :: bitset' के बारे में क्या? –

उत्तर

0

यहां यह है कि मैं इसे कैसे करूंगा, आशा करता हूं कि आपको यह उपयोगी लगेगा।

मुख्य समस्या यह है कि एनम झंडे और अन्य सभी प्रकारों के बीच अंतर कैसे करें। मैं एक अतिरिक्त टेम्पलेट प्रकार का उपयोग करता हूं, उदा। flag_type है, जो एक सदस्य केवल हमारे enum झंडे के लिए value होगा: जबकि enum झंडे के लिए यह एक EnumFlag प्रकार है जो operator| से दिया जाता है में शामिल होंगे

template<typename T> 
typename flag_type<T>::value operator | (T, T) 
{ 
    /* implementation */ 
} 

डिफ़ॉल्ट flag_type खाली

template<typename T> 
struct flag_type {}; 

है।

#define DECLARE_FLAG_TYPE(__type)        \ 
template<>             \ 
struct flag_type<__type> { using value = EnumFlag<__type>; } 

फिर, हम दोनों बाहरी दायरे में और कक्षाओं में enum झंडे को परिभाषित करने के लिए इसका इस्तेमाल कर सकते हैं:: यहाँ एक मैक्रो जो यह करता है

enum class Option 
{ 
    One = 1 << 0, 
    Two = 1 << 1, 
    Three = 1 << 2 
}; 
DECLARE_FLAG_TYPE(Option); 

class Class 
{ 
public: 
    enum class Option 
    { 
     Four = 1 << 0, 
     Five = 1 << 1 
    }; 
}; 
DECLARE_FLAG_TYPE(Class::Option); 
+0

धन्यवाद, यह मैंने लिखा मैक्रो से एक अच्छा सुधार है। यह काम करता हैं! : डी लेकिन फिर भी, मुझे कक्षा के बाहर ध्वज की घोषणा पसंद नहीं है। मुझे लगता है कि एक शुद्ध टेम्पलेट समाधान की आवश्यकता है। – Mathias

5

एंथनी विलियम्स तैयार कोड के साथ एक अच्छा acticle है: "Using Enum Classes as Bitfields"

शायद यह वही है जो आपने देखा है। मैंने देखा कि अन्य समाधानों के अंतर में, मैक्रो का उपयोग यहां नहीं किया जाता है - केवल शुद्ध टेम्पलेट्स।

सरल उदाहरण उनके समाधान का प्रदर्शन:

#include "bitmask_operators.hpp" 

enum class A{ 
    x=1,y=2 
     }; 

enum class B:unsigned long { 
    x=0x80000000,y=0x40000000 
     }; 

template<> 
struct enable_bitmask_operators<A>{ 
    static const bool enable=true; 
}; 

template<> 
struct enable_bitmask_operators<B>{ 
    static const bool enable=true; 
}; 

enum class C{x,y}; 


int main(){ 
    A a1=A::x | A::y; 
    A a2=a1&A::y; 
    a2^=A::x; 
    A a3=~a1; 

    B b1=B::x | B::y; 
    B b2=b1&B::y; 
    b2^=B::x; 
    B b3=~b1; 

    // C c1=C::x | C::y; 
    // C c2=c1&C::y; 
    // c2^=C::x; 
    // C c3=~c1; 
} 

bitmask_operators.hpp source code

+0

यह एक अच्छा लिंक है, लेकिन मैं प्रत्येक ध्वज के लिए एक नया टेम्पलेट विनिर्देश लिखने से पूरी तरह से संतुष्ट नहीं हूं। लेकिन उसने मुझे कुछ विचार दिए हैं (लेकिन वे असफल)। कुछ इस तरह: ... वर्ग EnumFlag: is_enum_flag ... EnumFlag :: मूल्य, टी> :: प्रकार> ऑपरेटर | (टी एल, टी आर) ... – Mathias

+0

@Mathias, इस कोड को देखें: https://github.com/grisumbras/enum-flags – DJm00n

0

यह समाधान किसी भी अतिरिक्त मैक्रो \ बिल्कुल वर्ग घोषणा Enum के लिए उपयोग कर की आवश्यकता नहीं है:

enum class MyEnum { Value1 = 1 << 0, Value2 = 1 << 1 }; 
using MyEnums = flags<MyEnum>; // actually this line is not necessary 

auto mask = Value1 | Value2; // set flags Value1 and Value 2 
if (mask & Value2) { // if Value2 flag is set 
    doSomething(); 
} 

https://github.com/grisumbras/enum-flags

संबंधित मुद्दे