Veroderung von enum-Werten



  • camper schrieb:

    Schonmal ios_base::fmtflags angeschaut? (Allgemein 17.5.2.1.3 Bitmask types)

    Nein, hab auch keinen Bock mir das jetzt zu suchen. Wenn da drin steht, dass man mit üblem rumcasten enum-Werte basteln soll/darf, die nicht mal im enum vorkommen, dann ist es Müll.


  • Mod

    Captain Obvious schrieb:

    camper schrieb:

    Schonmal ios_base::fmtflags angeschaut? (Allgemein 17.5.2.1.3 Bitmask types)

    Nein, hab auch keinen Bock mir das jetzt zu suchen. Wenn da drin steht, dass man mit üblem rumcasten enum-Werte basteln soll/darf, die nicht mal im enum vorkommen, dann ist es Müll.

    Dann solltest du dich evtl in
    Captain Oblivious umbenennen? j/k



  • Th69 schrieb:

    Statt dem static_cast<> könntest du auch einfach den Konstruktor verwenden:

    testfunc(NIBBLEBITS(BIT0 | BIT1));
    

    Das ist kein Konstruktor, sondern ein Function Style Cast, und damit semantisch equivalent zum C-Cast. Würde ich vermeiden.

    Nathan schrieb:

    constexpr NIBBLEBITS operator|(const NIBBLEBITS& lhs, const NIBBLEBITS& rhs)
    void testfunc(const NIBBLEBITS bit)
    

    Const-Wahn? Übergib Enumeratoren bitte per Value und zwar ohne const. Den Aufrufer interessiert es genau gar nicht, ob die Kopie konstant ist oder nicht.

    <💡>



  • Beitrag bitte löschen.



  • Hahaha, ja.

    Ernsthaft, willst du damit sagen, dass dieser Haufen Müll seit neuestem ok sein soll?

    constexpr NIBBLEBITS operator|(const NIBBLEBITS& lhs, const NIBBLEBITS& rhs)
    {
        return NIBBLEBITS(static_cast<int>(lhs) | static_cast<int>(rhs));
    }
    

  • Mod

    Captain Obvious schrieb:

    Hahaha, ja.

    Ernsthaft, willst du damit sagen, dass dieser Haufen Müll seit neuestem ok sein soll?

    constexpr NIBBLEBITS operator|(const NIBBLEBITS& lhs, const NIBBLEBITS& rhs)
    {
        return NIBBLEBITS(static_cast<int>(lhs) | static_cast<int>(rhs));
    }
    

    Nein.

    Ich behaupte, dass es seit Einführung von Operatorüberladung (noch vor der letzten Eiszeit) ein übliches Mittel ist, um (einigermaßen) sichere Flagtypen zu erhalten.



  • camper schrieb:

    Schonmal ios_base::fmtflags angeschaut? (Allgemein 17.5.2.1.3 Bitmask types)

    sieht nicht gut aus, File include\xiosbase

    #define _IOSskipws		0x0001
     #define _IOSunitbuf	0x0002
     #define _IOSuppercase	0x0004
    ...
    enum _Dummy_enum {_Dummy_enum_val = 1};	// don't ask
    enum _Fmtflags
    	{	// constants for formatting options
    	_Fmtmask = 0xffff, _Fmtzero = 0};
    static const _Fmtflags skipws = (_Fmtflags)_IOSskipws;
    static const _Fmtflags unitbuf = (_Fmtflags)_IOSunitbuf;
    static const _Fmtflags uppercase = (_Fmtflags)_IOSuppercase;
    


  • camper schrieb:

    Ich behaupte, dass es seit Einführung von Operatorüberladung (noch vor der letzten Eiszeit) ein übliches Mittel ist, um (einigermaßen) sichere Flagtypen zu erhalten.

    Habe ich so noch nicht gesehen, bzw. nicht wahrgenommen. Ich finde es höchst fragwürdig, wenn bei einer Aufzählung wie

    enum foo { bar = 1, baz = 2 };
    

    hin und wieder auch mal ein foo mit dem Wert drei auftauchen darf. Das kann Standard sein wie es will, es bleibt komisch.



  • ... denke, denke, denke ...

    Vorschlag:

    #include <iostream>
    
    enum NIBBLEBIT {BIT0 = 1, BIT1 = 2, BIT2 = 4, BIT3 = 8};
    typedef int NIBBLEBITS;
    
    void testfunc(NIBBLEBITS bits)
    {
        std::cout << bits << std::endl;
    }
    
    int main()
    {
        testfunc(BIT0);
        testfunc(BIT0 | BIT1);
    
        std::cin.get();
    
        return 0;
    }
    

  • Mod

    Captain Obvious schrieb:

    Ich finde es höchst fragwürdig, wenn bei einer Aufzählung wie

    enum foo { bar = 1, baz = 2 };
    

    hin und wieder auch mal ein foo mit dem Wert drei auftauchen darf.

    Ich fände es fragwürdig, wenn der numerische Wert jemals im Programm relevant werden sollte...

    dd++ schrieb:

    ... denke, denke, denke ...

    Vorschlag:

    #include <iostream>
     
    enum NIBBLEBIT {BIT0 = 1, BIT1 = 2, BIT2 = 4, BIT3 = 8};
    typedef int NIBBLEBITS;
     
    void testfunc(NIBBLEBITS bits)
    {
        std::cout << bits << std::endl;
    }
     
    int main()
    {
        testfunc(BIT0);
        testfunc(BIT0 | BIT1);
     
        std::cin.get();
     
        return 0;
    }
    

    Das hindert niemanden

    testfunc(3);
    

    zu schreiben, was sinnlos wäre.

    enum Flag1 { a = 1 << 0, b = 1 << 1, c = 1 << 2 };
    enum Flag2 { x = 1 << 0, y = 1 << 1, z = 1 << 2 };
    ... überladene Operatoren ...
    testfunc1(Flag1);
    testfunc2(Flag2);
    
    testfunc1(a|b); // ok
    testfunc1(x|y); // error: wäre sinnlos
    testfunc2(a|b); // error: wäre sinnlos
    testfunc2(x|y); // ok
    

    C++03 schrieb:

    17.3.2.1.2 Bitmask types [lib.bitmask.types]
    1 Several types defined in clause 27 are bitmask types. Each bitmask type can be
    implemented as an enumer- ated type that overloads certain operators, as an integer type, or as a
    bitset (23.3.5).

    2 The bitmask type bitmask can be written:

    enum bitmask {
    V0 = 1 << 0, V1 = 1 << 1, V2 = 1 << 2, V3 = 1 << 3, .....
    };

    static const bitmask C0(V0); static const bitmask C1(V1); static const bitmask C2(V2); static const
    bitmask C3(V3);
    .....

    bitmask operator& (bitmask X, bitmask Y)
    // For exposition only.
    // int_type is an integral type capable of
    // representing all values of bitmask
    { return static_cast<bitmask>( static_cast<int_type>(X) & static_cast<int_type>(Y)); }

    bitmask operator| (bitmask X, bitmask Y)
    { return static_cast<bitmask>( static_cast<int_type>(X) | static_cast<int_type>(Y)); }
    bitmask operatorˆ (bitmask X, bitmask Y)
    { return static_cast<bitmask>( static_cast<int_type>(X) ˆ static_cast<int_type>(Y)); }
    bitmask operator˜ (bitmask 😵
    { return static_cast<bitmask>(static_cast<int_type>(˜X)); }

    bitmask& operator&=(bitmask& X, bitmask Y)
    { X = X & Y; return X; }
    bitmask& operator|=(bitmask& X, bitmask Y)
    { X = X | Y; return X; }
    bitmask& operatorˆ=(bitmask& X, bitmask Y)
    { X = X ˆ Y; return X; }

    3 Here, the names C0, C1, etc. represent bitmask elements for this particular bitmask
    type. All such ele- ments have distinct values such that, for any pair Ci and Cj, Ci & Ci is
    nonzero and Ci & Cj is zero.

    4 The following terms apply to objects and values of bitmask types:

    — To set a value Y in an object X is to evaluate the expression X ï= Y.

    — To clear a value Y in an object X is to evaluate the expression X &= ˜Y.

    — The value Y is set in the object X if the expression X & Y is nonzero.

    Der Standard verlangt nat. nicht, dass eine Implementation tatsächlich enums hierfür verwendet.



  • camper schrieb:

    Ich fände es fragwürdig, wenn der numerische Wert jemals im Programm relevant werden sollte...

    Muss ja nicht.

    foo blubb = do_something();
    switch(blubb) { ... }
    

    reicht schon.

    Eine Aufzählung ist eine Aufzählung. Wenn da in eine Variable - wie auch immer - Werte reinkommen, die in der Aufzählung nicht vorhanden sind, dann ist das bestenfalls ein Hack. Wenn C++ Bitmasken unterstützen will, warum dann nicht richtig? Diese "Lösung" ist doch wieder dem übertriebenen bloß-keine-reservierte-Bezeichner-einführen-Wahn geschuldet. Wer das gut findet, der leidet am Stockholm-Syndrom.

    Edit: was du da gepostet hast, sieht ja fast aus wie der KotCode von Nathan. Hat sein Compiler unrecht, wenn er warnt?



  • #include <iostream>
    
    enum NIBBLEBITS {BIT1 = 1, BIT2 = 2, BIT3 = 4, BIT4 = 8};
    
    namespace {
        struct Ored
        {
            int mret;
    
            explicit Ored(NIBBLEBITS a, NIBBLEBITS b) : mret(a|b) {}
        };
    }
    
    Ored operator|(NIBBLEBITS n1, NIBBLEBITS n2)
    {
        return Ored(n1, n2);
    }
    
    void testfunc(NIBBLEBITS stuff)
    {
        std::cout << stuff << std::endl;
    }
    
    void testfunc(Ored stuff)
    {
        std::cout << stuff.mret << std::endl;
    }
    
    int main()
    {
        testfunc(BIT1);
        testfunc(BIT1|BIT2);
        testfunc(1); //geht nat. nicht...
    }
    

    Wie wärs damit? Jetzt die Klasse und den anonymen Namensraum in einen andere Übersetzungseinheit und bei belieben natürlich noch ergänzen und fertig...



  • Hallo,

    als Threadstarter melde ich mich nochmal.

    Danke an alle! Habe wieder was dazu gelernt.

    Und wie camper schon schrieb, bilde ich mit enums Flags ab. Dafür sind enums auf jedenfall besser als ints. (siehe Beitrag von camper)

    Gruss Ludger.



  • dd++ schrieb:

    Vorschlag:

    +1

    Wieso die Funktion nur auf das eine Enum begrenzen? Kann man doch auch wunderbar für andere numerische Werte benutzen.

    #include <iostream>
    
    enum NIBBLEBIT {BIT0 = 1, BIT1 = 2, BIT2 = 4, BIT3 = 8};
    
    void bitProof(int value)
    {
        if ((value & BIT0) == BIT0)   
            std::cout << "Bit 0 ist gesetzt" << std::endl; 
        if ((value & BIT1) == BIT1)   
            std::cout << "Bit 1 ist gesetzt" << std::endl;
        if ((value & BIT2) == BIT2)   
            std::cout << "Bit 2 ist gesetzt" << std::endl; 
        if ((value & BIT3) == BIT3)   
            std::cout << "Bit 3 ist gesetzt" << std::endl;
    }
    
    int main()
    {
        bitProof(BIT0);
        bitProof(BIT0 | BIT1);
    
        return 0;
    }
    

    Enums mag ich ehe nur als reine Aufzählung benutzen. Der Wert dahinter ist meistens egal, bei der BIT Sache drücke ich aber jedesmal ein Auge zu. Ist so doch halt schicker 🙂



  • KasF schrieb:

    Wieso die Funktion nur auf das eine Enum begrenzen?

    Typsicherheit (siehe camper). Man möchte nur Kombinationen der Enumeratoren erlauben.



  • @Captain Obvious
    Guck mal die Sache ist doch wirklich denkbar einfach.

    C++ erlaubt dir enum Typen zu definieren. Per Default haben die keinen operator | und keinen operator & definiert. Wenn man dann enumA | enumB schreibt werden die beiden Operanden erstmal in einen int verwandelt, und dann verodert. Das Ergebnis ist dann ein int , und um wieder nen enum draus zu machen müsste man mit static_cast draufhauen.

    Da man mit static_cast ziemlich viel Unsinn anstellen kann, erübrigt sich mMn. die Frage ob das nun eine weitere Sache ist wo man mit static_cast Unsinn machen kann oder nicht.

    Wenn man nen enum für Werte braucht deren Veroderung nicht sinnvoll ist, dann ist die Sache damit erledigt. Man definiert eben keine Operatoren für den enum Typ. Und wenn irgendwer meint mit static_cast einen (nicht im enum enthaltenen) Wert erzwingen zu müssen, dann ist er hübsch selbst schuld falls dabei Unsinn rauskommt.

    Wenn man aber typsichere Flags haben will, dann definiert man die besagten Operatoren eben, und der User kann ohne static_cast die Werte verodern oder verunden.

    Wenn man sich nun Sorgen macht ob der zugrundeliegende Typ wohl auch immer gross genug sei, dann kann man ja durchaus was machen. In C++ 03 kann man Dummy-Werte aufnehmen um einen minimalen Wertbereich zu erzwingen. Und in C++ 11 kann man den Typ explizit angeben.



  • KasF schrieb:

    Wieso die Funktion nur auf das eine Enum begrenzen? Kann man doch auch wunderbar für andere numerische Werte benutzen.

    #include <iostream>
     
    enum NIBBLEBIT {BIT0 = 1, BIT1 = 2, BIT2 = 4, BIT3 = 8};
     
    void bitProof(int value)
    {
    ...
    

    Achje.
    Der Sinn dahinter ist doch gerade dass man NICHT WILL dass die bitProof Funktion mit einem int aufgerufen werden kann.

    Weil man dann...

    enum WindowStyle
    {
        WindowStyle_None = 0,
        WindowStyle_Foo = 1 << 0,
        WindowStyle_Bar = 1 << 1,
        // ...
    };
    
    enum FileShareMode
    {
        FileShare_None = 0,
        FileShare_Read = 1 << 0,
        FileShare_Write = 1 << 1,
        FileShare_Delete = 1 << 2,
    };
    
    Window* CreateWindow(int width, int height, int flags);
    
    void Fun()
    {
        Window* w1 = CreateWindow(WindowStyle_Foo | WindowStyle_Bar, 640, 480); // Leider Unfug
        Window* w2 = CreateWindow(640, 480, FileShare_Read | FileShare_Write); // Ebenso
        // ...
    }
    

    ...schreiben kann. Und man will eigentlich nicht dass man das kann.
    In dem Beispiel ist noch relativ offensichtlich dass das Quatsch ist. Bei Funktionen die zwei oder mehr Flags Parameter nehmen ist es oft nicht mehr so offensichtlich.



  • hustbaer schrieb:

    ...

    Das ist mir schon bewusst. Ich bezog mich auch nur auf die bitProof-Funktionalität, die er mM nach eigentlich implementieren wollte.



  • Bitflag enums sind durchaus ein gaengiges Mittel. Seit C++11 sowieso.



  • KasF schrieb:

    hustbaer schrieb:

    ...

    Das ist mir schon bewusst. Ich bezog mich auch nur auf die bitProof-Funktionalität, die er mM nach eigentlich implementieren wollte.

    OK, in dem Fall ... sehe ich das ähnlich.
    Ich hatte angenommen dass das bloss ein unglücklich gewähltes Beispiel war.


Anmelden zum Antworten