Problem mit Bitoperatoren und enums



  • Entschuldigung für den Doppelpost, aber ich wollte keinen eigenen Thread für die kleine Frage erstellen.

    folgender Code:

    // Enum-Definition
    enum Style {STYLE1, STYLE2, STYLE3};
    // Funktionsdeklaration
    void Func(Style newStyle);
    
    // So geht's
    Func(STYLE2);
    // Das geht schief
    Func(STYLE1|STYLE2);
    // So geht's wieder
    Func(Style(STYLE1|STYLE2)); //<--
    

    In der letzten Anweisung werden der Funktion 2 Stile durch Veroderung übergeben. Dabei muss die Veroderung wieder in einen Stil umgewandelt werden. Allerdings verstehe ich nicht, wie das funktionieren soll, bzw. was da abläuft und was schlussendlich der Funktion dann übergeben wird.

    Danke schonmal für die Hilfe

    Don Quijote

    EDIT Phoemuex: Themen geteilt, daher passt das mit dem Doppelpost nicht mehr ganz



  • Don Quijote schrieb:

    Entschuldigung für den Doppelpost, aber ich wollte keinen eigenen Thread für die kleine Frage erstellen.

    Bitte immer für separate Fragen auch separate Threads. Das ist besonders hilfreich weil andere u.a. nach dem Titel suchen.

    Warum du probleme mit dem enum hast, ist ganz einfach: Ein enum dient dazu eine bestimmte Anzahl an Werten zuzulassen, es ist kein Flag. Zumal diese Kombination auch nicht zwangsweise das macht was du willst.

    Nehmen wir mal dein enum (und bitte die C/C++-Tags, und nicht die Code-Tags verwenden):

    // Enum-Definition
    enum Style {STYLE1, STYLE2, STYLE3};
    

    Das schreibe ich mal wie es wohl interpretiert wird:

    // Enum-Definition
    enum Style {
      STYLE1 = 0,
      STYLE2 = 1,
      STYLE3 = 2
    };
    

    Eine Oderverknüpfung zwischen 0x0000 (STYLE1) und 0x0001 (STYLE2) ergibt 0x0001 (STYLE2). Wohl kaum das gewünschte...

    Wenn du mit Flags arbeiten willst, solltest du
    a) Beim Enum die Wertigkeit angeben
    b) Einen Integralen Datentyp (int, long...) zur Übergabe verwenden

    // Enum-Definition
    enum Style {
      STYLE1 = 0x0001, // Bit 1 gesetzt
      STYLE2 = 0x0002, // Bit 2 gesetzt
      STYLE3 = 0x0004  // Bit 3 gesetzt
    };
    
    void Func(int newStyle);
    
    // Nun klappen auch die aufrufe:
    
    // So geht's
    Func(STYLE2);
    // Das geht schief
    Func(STYLE1|STYLE2);
    

    cu André



  • asc schrieb:

    Wenn du mit Flags arbeiten willst, solltest du [...] Einen Integralen Datentyp (int, long...) zur Übergabe verwenden

    Da würde ich aber lieber gleich den Enum-Typen Style als Parametertyp nehmen, wenn du ihn schon extra angibst. Wobei sich das bei der OR-Operation vielleicht nicht lohnt, da man casten müsste...

    asc schrieb:

    // Das geht schief
    Func(STYLE1|STYLE2);
    

    Was geht hier schief?

    Um mit Flags zu arbeiten, könnte man aber auch ein std::bitset verwenden.


  • Administrator

    Nexus schrieb:

    Don Quijote schrieb:

    // Das geht schief
    Func(STYLE1|STYLE2);
    

    Was geht hier schief?

    Ganz einfach:
    Aus STYLE1 | STYLE2 kommt ein int heraus und nicht wie erwartet ein enum . Daher muss man casten.

    Grüssli



  • Dravere schrieb:

    Ganz einfach:
    Aus STYLE1 | STYLE2 kommt ein int heraus und nicht wie erwartet ein enum . Daher muss man casten.

    Ja, das habe ich auch erwähnt. Aber da pumuckl die Funktion mit einem int -Parameter deklariert hat...


  • Administrator

    Nexus schrieb:

    Dravere schrieb:

    Ganz einfach:
    Aus STYLE1 | STYLE2 kommt ein int heraus und nicht wie erwartet ein enum . Daher muss man casten.

    Ja, das habe ich auch erwähnt. Aber da pumuckl die Funktion mit einem int -Parameter deklariert hat...

    Achso, ich dachte du hättest von Don Quijote kopiert. Ganz übersehen, das asc das auch drin hatte. Ich denke mal er hat einfach ein Copy&Paste gemacht und vergessen, die Kommentare rauszunehmen. Programmiererfehler Nummer 1 😃

    Grüssli



  • Dravere schrieb:

    Ich denke mal er hat einfach ein Copy&Paste gemacht und vergessen, die Kommentare rauszunehmen. Programmiererfehler Nummer 1 😃

    Man man man... Copy/Paste der Segen und Fluch der Programmierer.



  • asc schrieb:

    Wenn du mit Flags arbeiten willst, solltest du
    a) Beim Enum die Wertigkeit angeben
    b) Einen Integralen Datentyp (int, long...) zur Übergabe verwenden

    // Enum-Definition
    enum Style {
      STYLE1 = 0x0001, // Bit 1 gesetzt
      STYLE2 = 0x0002, // Bit 2 gesetzt
      STYLE3 = 0x0004  // Bit 3 gesetzt
    };
    
    void Func(int newStyle);
    
    // Nun klappen auch die aufrufe:
    
    // So geht's
    Func(STYLE2);
    // Das geht schief
    Func(STYLE1|STYLE2);
    

    cu André

    Gut, das hat mir schonmal etwas weiter geholfen, aber was würde denn bei

    Func(STYLE1|STYLE2)
    

    an die Funktion übergeben werden? beide Bits oder nur 0x0002?


  • Administrator

    Don Quijote schrieb:

    Gut, das hat mir schonmal etwas weiter geholfen, aber was würde denn bei

    Func(STYLE1|STYLE2)
    

    an die Funktion übergeben werden? beide Bits oder nur 0x0002?

    Es käme ein 0x0003 an, oder in Bits ausgedrückt:

    STYLE1     := 0000 0001 = 0x01 = 1
         STYLE2     := 0000 0010 = 0x02 = 2
    STYLE1 | STYLE2 := 0000 0011 = 0x03 = 3
    

    Grüssli



  • Ok vielen Dank euch allen für die schnelle Hilfe 🙂



  • void Func( int newStyle )
    

    womit aber die schoene typsicherheit der enums dahin waere...
    Das kann nicht die beste Loesung sein



  • Geht aber nicht anders. Außer du definierst für jeden Müll einen Enumeintrag A_AND_B, B_AND_C, A_AND_B_AND_C etc.



  • Fellhuhn schrieb:

    Geht aber nicht anders.

    diesen Satz in Verbindung mit C++ zu lesen ist eine wahre Seltenheit...
    Fuer alles Erdenkliche existieren Patterns, also warum nicht auch fuer sowas Elementares?



  • Du kannst natürlich das Ergebnis wieder zum Enum-Typ casten, dann geht es. Aber da du dann einen "ungültigen" Wert bekommen kannst, bringt dir der ganze Enum-Klumbatsch eh nichts mehr.



  • #include <iostream>
    
    enum A
    {
        AFlag1 = 1,
        AFlag2 = 2,
        AFlag3 = 4,
        AFlag4 = 8,
    };
    
    enum B
    {
        BFlag1 = 1,
        BFlag2 = 2,
        BFlag3 = 4,
        BFlag4 = 8,
    };
    
    template<typename Enum>
    class Flags
    {
        typedef void** Zero;
        int i;
    public:
        typedef Enum enum_type;
        Flags( const Flags& f ) : i( f.i ) {}
        Flags( Enum f ) : i( f ) {}
        Flags( Zero = 0 ) : i( 0 ) {}
    
        Flags& operator=( const Flags& f ) { i = f.i; return *this; }
        Flags& operator&=( int mask ) { i &= mask; return *this; }
        Flags& operator&=( uint mask ) { i &= mask; return *this; }
        Flags& operator|=( Flags f ) { i |= f.i; return *this; }
        Flags& operator|=( Enum f ) { i |= f; return *this; }
        Flags& operator^=( Flags f ) { i ^= f.i; return *this; }
        Flags& operator^=( Enum f ) { i ^= f; return *this; }
    
        operator int() const { return i; }
    
        Flags operator|( Flags f ) const { Flags g; g.i = i | f.i; return g; }
        Flags operator|( Enum f ) const { Flags g; g.i = i | f; return g; }
        Flags operator^( Flags f ) const { Flags g; g.i = i ^ f.i; return g; }
        Flags operator^( Enum f ) const { Flags g; g.i = i ^ f; return g; }
        Flags operator&( int mask ) const { Flags g; g.i = i & mask; return g; }
        Flags operator&( uint mask ) const { Flags g; g.i = i & mask; return g; }
        Flags operator&( Enum f ) const { Flags g; g.i = i & f; return g; }
        Flags operator~() const { Flags g; g.i = ~i; return g; }
    
        bool operator!() const { return !i; }
    
        bool testFlag( Enum f ) const { return i & f; }
    };
    
    #define DECLARE_FLAGS(Fl) \
    inline Flags<Fl::enum_type> operator|( Fl::enum_type f1, Fl::enum_type f2 ) \
    { return Flags<Fl::enum_type>(f1) | f2; } \
    inline Flags<Fl::enum_type> operator|( Fl::enum_type f1, Flags<Fl::enum_type> f2 ) \
    { return f2 | f1; }
    
    typedef Flags<A> AFlags;
    typedef Flags<B> BFlags;
    
    DECLARE_FLAGS( AFlags );
    DECLARE_FLAGS( BFlags );
    
    int needA( AFlags test )
    {
        return test;
    }
    
    int needB( BFlags test )
    {
        return test;
    }
    
    int main()
    {
        std::cout << needA( AFlag1 | AFlag2 ) << std::endl; // geht
        std::cout << needB( BFlag1 | BFlag2 ) << std::endl; // geht
        std::cout << needA( AFlag1 | BFlag2 ) << std::endl; // geht nicht
        std::cout << needB( 3 ) << std::endl; // geht nicht
    }
    

    "geht nicht" gibts nicht 😉

    (leichte Abaenderung der Implementation der Qt-Bibliothek, also nicht mein Werk)


Anmelden zum Antworten