C++11 binäres Und auf streng typisiertes enum



  • Hallo,
    ich kann keinen &-Operator auf den Wert eines streng typisierten enums anwenden:

    BOOST_SCOPED_ENUM_START(Flags) : uint32_t
    {
    	NoAlpha = 0,
    	Alpha1Bit = 1, /// BLP2 only.
    	Alpha4Bit = 4,  /// BLP2 only (DXT3 only).
    	Alpha = 8
    };
    BOOST_SCOPED_ENUM_END
    
    // Fehler: no match for operator& ...
    else if (this->flags() & Blp::Flags::Alpha ...
    

    Ist das allgemein nicht möglich oder muss ich explizit casten? Google hat nur merkwürdige Sachen zum alten Standard ausgespuckt.



  • Eigenen Operator schreiben

    zB

    Flags operator&(Flags lhs, Flags rhs) {
      return static_cast<Flags>(static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs));
    }
    

    Wobei man natürlich anstelle unsigned den "underlying type" nehmen sollte. Kann man den über ein Trait erfahren?

    edit: std::underlying_type

    Foo operator&(Foo lhs, Foo rhs) {
      return static_cast<Foo>(static_cast<std::underlying_type<Foo>::type>(lhs)
                              &
                              static_cast<std::underlying_type<Foo>::type>(rhs));
    }
    

    Geht aber erst mit GCC 4.7



  • Andererseits ist es natürlich gefährlich, weil man so ein Foo bekommen kann, dass aber keine Darstellung als enum hat. Vielleicht ist es in dem Fall einfach besser Integer-Konstanten zu nehmen.



  • Danke,
    aber ehrlich gesagt finde ich es ziemlich schwachsinnig, dass man die enum-Werte nicht implizit zu ihrem "underlying type" konvertieren kann. Umgekehrt macht es ja schon eher Sinn, aber wenn ich schon den Operator verwende ...
    So müsste ich halt jetzt für alle streng typisierten enums so einen Operator schreiben.



  • Barade schrieb:

    aber ehrlich gesagt finde ich es ziemlich schwachsinnig, dass man die enum-Werte nicht implizit zu ihrem "underlying type" konvertieren kann.

    Einer der Kritikpunkte an den alten Enums war die Typsicherheit...

    Barade schrieb:

    So müsste ich halt jetzt für alle streng typisierten enums so einen Operator schreiben.

    Davon rate ich ab, da wie schon erwähnt enum -Werte entstehen können, die nicht gültig sind.

    Aber du kannst z.B. sowas machen (nebenbei hat das weitere Vorteile wie z.B. zwingende Initialisierung).

    template <typename E>
    class Flag
    {
    public:
    	typedef E EnumType;
    	typedef typename std::underlying_type<E>::type IntType;
    
    public:
    	Flag(EnumType enumerator)
    	: mValue(static_cast<IntType>(enumerator))
    	{
    	}
    
    	explicit Flag(IntType integer)
    	: mValue(integer)
    	{
    	}
    
    	IntType Get() const
    	{
    		return mValue;
    	}
    
    	explicit operator bool () const
    	{
    		return Get() != 0;
    	}
    
    private:
    	IntType mValue;
    };
    
    template <typename E>
    Flag<E> operator& (Flag<E> lhs, Flag<E> rhs)
    {
    	return Flag<E>(lhs.Get() & rhs.Get());
    }
    
    template <typename E>
    Flag<E> operator| (Flag<E> lhs, Flag<E> rhs)
    {
    	return Flag<E>(lhs.Get() | rhs.Get());
    }
    


  • Ach so, tschuldigung, das mit dem resultierenden Typ habe ich ganz verpennt. Gibt es eventuell auch eine Möglichkeit, mit einem einfachen Check zu überprüfen, ob es ein gültiger enum-Wert ist und andernfalls eine Exception zu werfen (aber ich schätze mal, man müssten ihn ja jedes Mal mit allen gültigen Werten vergleichen).
    Bei dieser Klassenimplementation mit zwingender Initialisierung wird aber doch auch nicht überprüft, ob es ein gültiger enum-Wert ist, wenn ich das richtig sehe.

    edit:
    Eigentlich müsste der Operator ja nur bool zurückgeben, zumindest bei dem & und es spielt dabei ja keine Rolle, ob ein gültiger Wert zurückgegeben wird.
    Bei | siehts schon wieder anders aus.

    edit2:
    Gibts GCC 4.7 überhaupt schon? Ich sehe da nur was von 4.6 auf deren Website.

    edit3:
    Und noch ein edit. Gibt es die Möglichkeit, ein scoped enum mit enthaltenem Typ zu deklarieren, das diese Beschränkung nicht hat? Also so ein Zwischending.



  • Barade schrieb:

    Bei dieser Klassenimplementation mit zwingender Initialisierung wird aber doch auch nicht überprüft, ob es ein gültiger enum-Wert ist, wenn ich das richtig sehe.

    Spielt aber keine Rolle, da der Wert als Integer abgespeichert wird. Und die Schnittstelle bietet auch keine Möglichkeit, den originalen Typ ohne Cast zu erhalten.

    Barade schrieb:

    Eigentlich müsste der Operator ja nur bool zurückgeben, zumindest bei dem & und es spielt dabei ja keine Rolle, ob ein gültiger Wert zurückgegeben wird.

    Grundsätzlich kann man ja den resultierenden Typen weiterverwenden. Ausserdem gibt auch der ursprüngliche operator& keinen boolschen Wert zurück, sondern eine Ganzzahl (sowie auch die Operanden Ganzzahlen sind). Analog hier.


  • Mod

    Barade schrieb:

    edit2:
    Gibts GCC 4.7 überhaupt schon? Ich sehe da nur was von 4.6 auf deren Website.

    4.7 ist noch in Entwicklung, Release wird voraussichtlich im März oder April kommen. GCC 4.7.0 Status Report



  • Nexus schrieb:

    Barade schrieb:

    Bei dieser Klassenimplementation mit zwingender Initialisierung wird aber doch auch nicht überprüft, ob es ein gültiger enum-Wert ist, wenn ich das richtig sehe.

    Spielt aber keine Rolle, da der Wert als Integer abgespeichert wird. Und die Schnittstelle bietet auch keine Möglichkeit, den originalen Typ ohne Cast zu erhalten.

    Ja, stimmt auch wieder.

    Nexus schrieb:

    Barade schrieb:

    Eigentlich müsste der Operator ja nur bool zurückgeben, zumindest bei dem & und es spielt dabei ja keine Rolle, ob ein gültiger Wert zurückgegeben wird.

    Grundsätzlich kann man ja den resultierenden Typen weiterverwenden. Ausserdem gibt auch der ursprüngliche operator& keinen boolschen Wert zurück, sondern eine Ganzzahl (sowie auch die Operanden Ganzzahlen sind). Analog hier.

    Also die Klasse sieht schon ganz praktisch aus und das mit dem normalen operator& weiß ich natürlich schon, aber wie gesagt, es war nur auf meinen Fall bezogen. Ich weiß noch nicht genau, wie ich das jetzt am besten umsetze. Die Sache war nur die, dass sich BOOST_SCOPED_ENUM dadurch inkompatibel zum neuen Standard verhält und ich den somit nicht ohne Weiteres benutzen kann.

    Gut, gibt es bei den Open-Source-Compilern irgendwelche Empfehlungen oder Einschätzungen, wenn man auf den neuen Standard umsteigen will? Ich will das jetzt natürlich zu sehr ausweiten, daher genügt auch eine knappe Antwort. Ich bin mit dem gcc eigentlich ganz zufrieden, habe aber gelesen, dass llvm/clang auch nicht schlecht sein soll bzw. gibt es überhaupt noch andere?


Anmelden zum Antworten