Enums in C++



  • Moin,
    und zwar habe ich mal wieder ein Problem / eine Frage.

    Ich erstelle gerade eine Klasse "Card". Diese Klasse hat 2 private member:

    Rank rank;
    Suit suit;
    

    Meine Frage ist jetzt wie ich das am besten umsetze. In Java habe ich einfach anstatt 2 Klassen Rank und Suit, 2 Enums erstellt:

    Beispiel in Java für das Enum Suit:

    public enum Suit {
    	HEARTS,
    	DIAMONDS,
    	CLUBS,
    	SPADES;
    
    	public String shortName(Suit suit) {
    		String s = "";
    
    		switch(suit) {
    		case HEARTS:
    			s = "h";
    			break;
    		case DIAMONDS:
    			s = "d";
    			break;
    		case CLUBS:
    			s = "c";
    			break;
    		case SPADES:
    			s = "s";
    			break;
    		}
    
    		return s;
    	}
    }
    

    In der Klasse "Deck" konnte ich dann dem entsprechend die Karten wie folgt erzeugen:

    private void createStack() {
    		for(Suit suit : Suit.values()) {
    			for(Rank rank : Rank.values()) {
    				stack.add(new Card(rank, suit));
    			}
    		}
    	}
    

    Wie würde ich so etwas in C++ Designen?

    Gruß
    Byte



  • Du schaust in deinem Buch im Stichwortverzeichnis unter enum nach.



  • manni66 schrieb:

    Du schaust in deinem Buch im Stichwortverzeichnis unter enum nach.

    Das hilft in der Regel nicht. Java-Enums können viel mehr als C++-Enums. Ich finde auch den hier gezeigten Java-Code, insbesondere die Methode shortName, schlecht. Ich würde in Java dem Enum dann einen Member geben, der dem kurzen Namen entspricht und diesem im Konstruktor setzen. Die Funktion shortName sollte dann einfach diesen Member-String returnen. Dann ist das switch weg. In C++ geht sowas leider nicht (einfach). Das dichteste, was ich mir dazu vorstellen kann, sind Klassen mit privatem Konstruktor und (hier) mit den 4 statischen Variablen HEARTS, DIAMONDS, CLUBS und SPADES.

    Auch Iterieren über alle Enum-Member geht nicht (allgemein). Man muss sich also alles von Hand bauen. Oder man macht so unschöne Dinge wie ein END_ENUM einzufügen und loopt dann von [0, static_cast<int>(END_ENUM)[ und castet dann die Loop-Variable zu dem enum. Geht aber nur, wenn die enum-Werte alle direkt hintereinander stehen und es fühlt sich irgendwie falsch an.

    Also: ich wäre auch dankbar über jeden Hinweis, wie man enum classes in C++ sinnvoll nutzen kann - ich empfinde sie als extrem einschränkend.



  • wob schrieb:

    manni66 schrieb:

    Du schaust in deinem Buch im Stichwortverzeichnis unter enum nach.

    Das hilft in der Regel nicht. Java-Enums können viel mehr als C++-Enums.

    Das mag sein, der Beispielcode kann aber doch wohl leicht in C++ konvertiert werden.



  • Wie denn?

    Konkret: wie übersetzt du

    for(Suit suit : Suit.values())
    

    nach C++?



  • Jetzt willst du mich aber herausfordern 😉

    #include <iostream>
    
    enum A { EINS, ZWEI, DREI, ENDE };
    const char* names[] = { "eins", "zwei", "drei", "nicht verwendet" };
    
    int main()
    {
            for( auto i = 0; i < ENDE; ++i ) {
                    std::cout << names[i] << ",";
            }
    
            std::cout << "\n";
    }
    


  • Ah, ok. Das mit dem Extra-Endelement hatte ich oben auch geschrieben. Nur: ich finde diese Lösung alles andere als schön.

    Zumal:

    enum MeinEnum { A = 0, B = 199, ENDE };
    

    Und schon geht der Code nicht mehr.

    Jetzt habe ich noch

    enum MeinEnum2 { C, D, E, ENDE };
    

    Whoops, jetzt habe ich 2x ENDE. Also nehmen wir enum class.

    Also mal das auspobieren:

    enum class MeinEnum { A = 0, B = 199, ENDE };
    for( auto i = 0; i < static_cast<int>(MeinEnumC::ENDE); ++i ) { 
        whoops_i_gleich_1_macht_boom(i);
    }
    

    Hm, Code wird nicht besser, sondern nur unübersichtlicher mit dem Cast.

    Also ich bin nicht glücklich mit diesen "Lösungen".

    (und bin raus für heute, muss jetzt feiern gehen)



  • Ich würde das vielleicht einfach so machen:

    struct Card {
        char suit;  // in Manchen Spielen sind bestimmte Suits stärker als andere, evtl. auch hier ein plain enum, oder halt eine extra suitcmp Funktion schreiben
        int rank;  // evtl. auch enum
    };
    std::vector<Card> deck;
    constexpr int Ace = 1;
    constexpr int King = 13;
    for (int i = Ace; i <= King; ++i) {
        deck.push_back({'h', i});  // Hearts
        deck.push_back({'d', i});  // Diamonds
        deck.push_back({'s', i});  // Spades
        deck.push_back({'c', i});  // Clubs
    }
    

    Das sieht voll C-like aus, aber es ist einfach schlicht. Ich kann mir leider keine Template magic einfallen lassen, um dem enum class irgendwie die Java-Funktionalität zu geben. Folgenden Ansatz finde ich z.B. nicht so toll, aber was besseres fällt mir nicht ein:

    enum class Suit : char {
        Hearts = 'h',
        Diamonds = 'd',
        Spades = 's',
        Clubs = 'c',
    
        Begin = Hearts,
        End = '\0'
    };
    
    Suit& operator++(Suit& s)
    {
        switch (s) {
        case Suit::Hearts: return s = Suit::Diamonds;
        case Suit::Diamonds: return s = Suit::Spades;
        case Suit::Spades: return s = Suit::Clubs;
        default: return s = Suit::End;
        }
    }
    
    char shortName(Suit suit)
    {
        return static_cast<char>(suit);
    }
    
    enum class Rank {
        Ace = 1,
        Two, Three, Four, Five, Six, Seven, Eigth, Nine, Ten,
        Jack, Queen, King,
    
        Begin = Ace,
        End = King + 1,
    };
    
    Rank& operator++(Rank& r)
    {
        if (r < Rank::End)
            return r = static_cast<Rank>(static_cast<int>(r) + 1);
        return r = Rank::End;
    }
    
    using Card = std::pair<Suit, Rank>;
    using Deck = std::vector<Card>;
    
    Deck createStack()
    {
        Deck deck;
        for (Suit s = Suit::Begin; s != Suit::End; ++s)
            for (Rank r = Rank::Begin; r != Rank::End; ++r)
                deck.emplace_back(s, r);
        return deck;
    }
    

    Viel Spaß beim auseinanderreißen 🙂 Die Begin/End enum values sind hässlich und fehleranfällig. Außerdem muss man für jedes Enum speziell den pre-increment overloaden, auch fehleranfällig.

    LG



  • wob schrieb:

    Ah, ok. Das mit dem Extra-Endelement hatte ich oben auch geschrieben. Nur: ich finde diese Lösung alles andere als schön

    Naja, es ist ein popliges Kartenspiel.


Anmelden zum Antworten