1

I'm trying to learn C++ by converting a few Java classes I made a while ago. They represent a playing card and a deck of cards. I'm using an enum for the values and suits as such:

enum Suits{SPADES, CLUBS, HEARTS, DIAMONDS};

enum Values{TWO, THREE, FOUR, FIVE, 
            SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE};

In both my Java and C++ Card class I have methods: getValue() and getSuit() which obviously return the value and suit respectively.

My Java DeckofCards class is pretty simple:

public class DeckofCards {

private Card card; 
private String value;
private String suit;
private List<Card> deck = new ArrayList<Card>();

//DeckofCards constructor
public DeckofCards(){
    for (Suits s : Suits.values()) {
        for(Values v : Values.values()){
            card = new Card(v,s);
            deck.add(card);
        }  
    }
}

//shuffles the deck
public void shuffleDeck(){
    Collections.shuffle(deck);
}

//prints the deck of cards
public void printDeck(){

    for(int i = 0; i<deck.size(); i++){
        card = deck.get(i);
        value = card.getValue().toString();
        suit = card.getSuits().toString();
        System.out.println(value + " of " + suit);
    }

}

//draws a card from the deck
public Card drawCard(){
    try{
        card = deck.get(0);
        deck.remove(0);
        //return card;
    }
    catch(IndexOutOfBoundsException e){
        System.err.println("Deck is empty");
        System.exit(0);
    }
    return card;
}

} 

My problem is implementing the printDeck() method in C++, specifically getting the string representation of the enum values. I know I can't simply do getValue().toString() So my idea after doing some research on the matter was to make two std::string[] that look the same as the two enums and then use the getValue() and getSuit() to generate an int (since that seems to be the behavior) and pass that into the array to get the string representation.

However I'm now thinking that it might be better to add two more methods in my Card class:

std::string getValue(int i) and likewise for suit

and use a case statement to return a string value based on the int so that other classes can easily get the string representation. This seems redundant though. Can anyone provide any suggestions on how to do this?

Ryan Sayles
  • 3,389
  • 11
  • 56
  • 79
  • Why not overload `ostream& operator<<(ostream&,Suit)` if you just want to print it? You also get a string back out using `ostringstream.` – user783920 Mar 20 '14 at 18:16
  • See http://stackoverflow.com/q/201593/10077. We're using [X-macros](http://stackoverflow.com/a/201770/10077) where I work. – Fred Larson Mar 20 '14 at 18:21

2 Answers2

2

You can use new C++11 enum classes (i.e. scoped enums), and define a function that takes such an enum class as input parameter, and returns the corresponding string representation for the input enum value.

e.g.:

#include <assert.h>     // For assert()
#include <iostream>     // For console output
#include <string>       // For std::string

enum class Suits {
    SPADES, CLUBS, HEARTS, DIAMONDS
};

std::string toString(Suits s) {
    switch (s) {
    case Suits::SPADES:     return "Spades";
    case Suits::CLUBS:      return "Clubs";
    case Suits::HEARTS:     return "Hearts";
    case Suits::DIAMONDS:   return "Diamonds";
    default:
        assert(false);
        return "";
    }
}

int main() {
    std::cout << toString(Suits::CLUBS) << std::endl;
}

You can do a similar thing for the Values enum.

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • Do I still define this in my card.hpp header? Or will this be separate? – Ryan Sayles Mar 20 '14 at 18:37
  • @rsay3: The enum class definition must go in the card.hpp header (since clients will use this enum class). Then you're free to either 1. just insert the `toString()` function/method prototype in the header, and its definition in the .cpp file, or 2. put also the definition in the header, using the `inline` keyword (to avoid violations of the "one definition rule"). You may also want to read "[_C++ classes without .cpp file?_](http://stackoverflow.com/a/22479286/1629821)". – Mr.C64 Mar 20 '14 at 18:40
  • Ok, and would `toString()` be a non-member function? – Ryan Sayles Mar 20 '14 at 18:44
  • @rsay3: I don't how you organized your code. If you have the `Suits` enum class as a nested class of `DeckOfCards`, then you may want to make `toString()` a `static` member function of `DeckOfCards` as well. (I suggest using `static` in this case, since this `toString()` overload is not specific to any instance of `DeckOfCards`, i.e. it does not depend on the internal state of `DeckOfCards`: you just pass an enum value to it, and it returns the corrsponding string.) – Mr.C64 Mar 20 '14 at 18:47
  • My `Suits` and `Values` are not in my `DeckofCards` class, they are only defined in the header of my `Cards` class – Ryan Sayles Mar 20 '14 at 18:51
  • @rsay3: If you had everything under a `namespace`, you may want to define the `toString()` functions there. If this is just a test program (not production quality code), with your custom definitions just in the global namespace, you may either leave `toString()` in the global namespace, or if you use it inside `DeckOfCards`, just make `toString()` a static member of this class. – Mr.C64 Mar 20 '14 at 18:59
1

In C++ there's no metadata around enums, so if you want a string version you need to write a converter yourself.

I'd probably go with something like this, this doesn't compile but you get the rough picture.

enum Suit { ... }
enum Values { ... }

Class card {
public:
   static std::string getText(Suite s) { switch(s) case CLUBS: return "clubs"; ... }
   static std::string getText(Colour c) { switch(c) case ONE: return "one"; ... }

   card(Suite s, Colour c) : mSuite(s), mColour(c) {}
   std::string getText() const {
       stringstream ss;
       ss << getText(mColour) << " of " << getText(mSuits);
       return ss.str();
   }
};
ostream& operator<<(ostream& stream, const card& c) {
    stream << c.getText();
}
dutt
  • 7,909
  • 11
  • 52
  • 85