enum verunden



  • Hallo

    Also ich fang am besten gleich mal mit Code an:

    enum MyEnum
    {
      val0 = 0,  // 000
      val1 = 1,  // 001
      val2 = 2,  // 010
      val3 = 3,  // 011
      val4 = 4,  // 100
      val5 = 5,  // 101
      val6 = 6,  // 110
      val6 = 7,  // 111
    }
    

    so nun erstelle ich 2 Objekte

    MyEnum first  = val1;
    MyEnum second = val2;
    

    wenn ich diese nun verunde:

    MyEnum third = first & second;
    

    erhalte ich nen Compilerfehler: '=': 'int' kann nicht in 'MyEnum' konvertiert werden.

    Wieso dass denn? Sind doch alle vom typ MyEnum.



  • Zur Auswertung des Ausdrucks wird zuerst ein Konvertierung zu int durchgeführt. Das muss du explizit wieder zu deinem enum casten.



  • und eine andere Möglichkeits gibts nicht?



  • y-vonne schrieb:

    und eine andere Möglichkeits gibts nicht?

    doch, so:

    #include <iostream>
    
    struct MyEnum
    {
        explicit MyEnum( int val ) : m_val( val ) {}
        MyEnum& operator&=( MyEnum b )
        {
            m_val &= b.m_val;
            return *this;
        }
        friend std::ostream& operator<<( std::ostream& out, MyEnum e )
        {
            return out << "val" << e.m_val;
        }
    private:
        int m_val;
    };
    MyEnum operator&( MyEnum a, MyEnum b )
    {
        return a &= b;
    }
    
    namespace
    {
        const MyEnum val0(0);
        const MyEnum val1(1);
        const MyEnum val2(2);
        const MyEnum val3(3);
        const MyEnum val4(4);
        const MyEnum val5(5);
        const MyEnum val6(6);
        const MyEnum val7(7);
    }
    
    int main()
    {
        using namespace std;
        MyEnum first  = val1;
        MyEnum second = val2;
        MyEnum third = first & second; // jetzt geht es
        cout << "third= " << third << endl;
        return 0;
    }
    


  • Aha. Super danke.



  • Hier hat c++ zum glück eine Hintertür gelassen.

    Man kann INT auch nach einen ENUM casten.
    (Ist glaube ich auch so vorgesehen)

    MyEnum third = MyEnum(first & second);
    

    Lichlein



  • Werner_logoff schrieb:

    doch, so:

    Lichtlein schrieb:

    Man kann INT auch nach einen ENUM casten.

    int und enum. INT und ENUM gibt es nicht.

    Die böse Falle an beiden Lösungen ist, dass man Werte basteln kann, die in der Enumeration überhaupt nicht vorkommen. Werners Code gibt auch dann "val0" aus, wenn es überhaupt kein val0 gibt.

    Man kann ein int als enum verkleiden, wie von Lichtlein vorgeschlagen. Aber wenn der Wert nicht in der Enumeration vorkommt, ist das Ergebnis undefiniert. Auch hier einfach mal val0 aus der Enumeration löschen, und schon ist das Ergebnis von val1 & val2 nur noch Schrott.



  • Nukularfüsiker schrieb:

    Die böse Falle an beiden Lösungen ist, dass man Werte basteln kann, die in der Enumeration überhaupt nicht vorkommen. Werners Code gibt auch dann "val0" aus, wenn es überhaupt kein val0 gibt.

    Ja sicher, deshalb ist der implizite cast auch verboten.

    Man kann aber als Programmierer von struct MyEnum dafür sorgen, dass die UND-Operation nur gültige Werte liefert, bzw. bei ungültigen Werten in ein wie immer geartetes Fehlerhandling übergeht.
    Spätestens der Konstruktor muss prüfen, ob das 'val', welches da daher kommt, ok ist.



  • Warum legst Du die Werte der Konstanten denn explizit fest, wenn sie sowieso bei 0 beginnen und dann um 1 inkrementiert werden?

    lg, freakC++



  • Warum nimmst du nicht gleich int , wenn du rechnen willst?



  • Die Motivation für Enumaration ist doch meiner Meinung nach dem Compiler Feste Werte mitzuteilen. Dazu hat man drei Möglichkeiten:
    #define, enum und const int (int als beispiel)

    Besonders in Hardware nahen Bereich gibt es auch BitFelder die man zum besseren Verständnis mit Namen Declariert.

    Hat bestimmt schon jeder gesehen:

    #define   UART_SEND  0x01
    #define   UART_ERROR 0x02
    #define   UART_REQ   0x03
    usw.
    

    Von defines soll man halten was man will. 🙂

    Nehmen wir also const int (oder sonstwas)

    const int UART_SEND  0x01;
    const int UART_ERROR 0x02;
    const int UART_REQ   0x03;
    

    Ist möglich aber man kann dann in C++ nicht mehr mit dem Type Arbeiten.

    Jetzt noch die Möglichkeit 3 enum:

    enum eUart
    {
    UART_SEND  = 0x1,
    UART_ERROR = 0x2,
    UART_REQ   = 0x3
    };
    

    So jetzt habe ich einen Type und passende werte für jedes Bit.

    Das Problem ist es wenn ein UART Register auslesen wird. Da hat man mit Sicherheit einen Wert der nicht in einen define, const oder enum vorkommt.

    Genau so Problem fälle hat man bei C++ bedacht und deshalb lässt der Compiler auch den "enum::constructor" zu bz. eUart(int RegisterInhalt) ohne eine Warnung zu geben das hier zwei unpassende Werte zusammen gesetzt werden.

    Klar ist das man damit immer vorsichtig umgehen sollte und in einem switch/case Konstrukt für enum's immer ein assert im default Zweig haben sollte.

    Lichtlein



  • Lichtlein schrieb:

    Genau so Problem fälle hat man bei C++ bedacht und deshalb lässt der Compiler auch den "enum::constructor" zu bz. eUart(int RegisterInhalt) ohne eine Warnung zu geben das hier zwei unpassende Werte zusammen gesetzt werden.

    Das ist kein Konstruktor oder was Ähnliches, sondern ein Cast. Man könnte statt eUart(myInt) ebenso gut (eUart)myInt schreiben.

    Und natürlich kann man mit Casts Typsicherheit aushebeln. Ich kann ja auch int(myPointer) schreiben, und es kompiliert ohne Warnung. Nur geht das am Konzept von enum vorbei, welche eine Anzahl festgelegter Zustände (Enumeratoren) zulassen und beim Rest explizite Konvertierungen erforderlich machen. Wenn du nämlich dauernd rechnest und castest, bist du nicht mehr weit von int entfernt.

    Mich wundert es, dass der folgende Vorschlag noch nicht gebracht wurde. Ich würde allerdings auch eher zur Klasse tendieren, weil dann die potentiell fehleranfällige implizite Konvertierung zu integralen Typen nicht mehr vorhanden ist.

    MyEnum operator& (MyEnum lhs, MyEnum rhs);
    

Anmelden zum Antworten