2015-03-06 7 views
5

Ich versuche, typsichere C++ - Flags mit Vorlagen zu erstellen. Ich möchte auch unterscheiden zwischen ein Flag und Flag s (als Null, eine oder viele Flags).Vorlage für Typ sicher C++ 11 enum Klasse Flags

Die folgende Lösung funktioniert gut, mit Ausnahme von EnumFlag<T> operator | (T, T), die alle | -Operationen auf enums zum Rückgabetyp EnumFlag verursacht. Das bricht viel Code. Irgendwelche Tricks um das zu beheben? In meinem Code mache ich folgendes, jedoch ist eine harte Codierung Option hier keine Option. Wie mache ich das generisch?

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

ändern, dies zu ...

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

... von Ursache bricht alles. Ich möchte so etwas (nicht Compilabel-Code). Oder irgendeine andere bessere Idee!

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

komplette übersetzbar Code:

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 <

Der eigentliche Code wird viel mehr Operatoren enthalten, aber dies ist genug, um das Problem zu veranschaulichen.

Eine weniger aufdringliche Lösung ist dies (aber immer noch zu aufdringlich)

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)); 
} 

Gott nicht genug, dann EnumFlag(const std::underlying_type<T> &flags) vorhanden sein muss und ich Typ Safty verlieren. Außerdem möchte ich, dass die globalen Operatorüberladungen nur für die tatsächlich benötigten Typen erstellt werden. Macros ist auch kein Gott, weil ich die Deklaration von EnumFlag s in Klassen erlauben will. Die globalen Überladungen können nicht vorhanden sein, daher benötige ich zwei Makroaufrufe an unterschiedlichen Positionen, um sie unter EnumFlag zu erstellen.

Die Lösung muss rein C++ 11/stl sein.

+0

Würde von 'EnumFlag keine nicht-explizite Konvertierung Hinzufügen ' auf 'T 'behebt die Probleme, die durch 'EnumFlag operator | (T l, T r) "? – Pradhan

+0

Ich denke, das ist ein Workaround, Erstellen von 'EnumFlag' Instanzen, wenn Sie nicht brauchen, nur um zurück zu konvertieren. Dies erlaubt auch den Aufruf von 'foo (const Option & o)' mit einer Instanz von 'Options'. Daher verliert Typ Sicherheit. Innerhalb 'foo' haben wir vielleicht einen Schalter, der alle Fälle behandelt, immer noch wird nichts passieren ... – Mathias

+0

Was ist mit' std :: bitset', um deine Flaggen zu halten? –

Antwort

0

Hier ist, wie ich es tun würde, hoffe, dass Sie es nützlich finden.

Das Hauptproblem besteht darin, wie Enum-Flags und alle anderen Typen zu unterscheiden. Ich würde einen zusätzlichen Schablonentyp verwenden, z. flag_type, die ein Mitglied value nur für unsere Enum Fahnen haben würden:

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

Standard flag_type ist leer

template<typename T> 
struct flag_type {}; 

während für ENUM-Flags wird es enthält eine EnumFlag Art, die aus operator| zurückgeführt wird.Hier ist ein Makro, das tut es:

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

Dann wir es verwenden können, um Enum Fahnen in beide äußeren Umfang zu definieren und in den Klassen:

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

Danke, das ist eine nette Verbesserung von dem Makro, das ich geschrieben habe. Es klappt! : D Aber ich mag es trotzdem nicht, die Flagge außerhalb der Klasse zu deklarieren. Ich denke, eine reine Vorlagenlösung wird benötigt. – Mathias

5

Anthony Williams einen guten jeweiligen Artikel mit Ready-Code hat: "Using Enum Classes as Bitfields".

Vielleicht ist es genau das, was Sie gesucht haben. Im Unterschied zu anderen Lösungen, die ich gesehen habe, werden Makros hier nicht verwendet - nur reine Vorlagen.

Einfaches Beispiel demonstriert seine Lösung:

#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

Das ist ein guter Link, aber ich bin nicht völlig damit zufrieden, eine neue Template-Spezifikation für jede Flagge schreiben zu müssen. Aber es hat mir einige Ideen gegeben (aber sie haben versagt). Etwas wie folgt aus: ... Klasse EnumFlag: is_enum_flag ... EnumFlag :: Wert, T> :: type> Operator | (T l, T r) ... – Mathias

+0

@Mathias, sehen Sie sich diesen Code an: https://github.com/grisumbras/enum-flags – DJm00n

0

Diese Lösung keine zusätzlichen Makros erfordern \ mit haupt Enum Klassendeklaration:

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