1

I'm porting a card game application I developed in Java to C++, where the rank and suit of the card object are generic (to be able to use more than one specific set), and the card itself is generic also (to be bale to use poker cards, tarot card, etc).

I'm getting compile errors in the Dealer class (a template), which takes the card object (another template) using CodeBlocks 13.12 "codeblocks-13.12mingw-setup-TDM-GCC-481"

Card class (abstract but implements shared functionality all cards must have)

#ifndef CARD_H
#define CARD_H

#include <iostream>
#include <fstream>
#include <cassert>
using namespace std;

enum BJRank
{
    Ace,
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King,
    END_OF_RANKS
};
enum GenericSuit
{
    Clubs,
    Diamonds,
    Hearts,
    Spades,
    END_OF_SUITS
};

template <class U, class V>
class Card {
    private:
        U itsRank;
        V itsSuit;
    protected:
        Card(U newRank = Jack, V newSuit = Spades):
        itsRank(newRank), itsSuit(newSuit)
        {
            //s_numberPlayers++;
            //cout<<"Number of cards available is: " << s_numberPlayers << "\n";
            ofstream myfile ("C:/Temp/cardLogFile.txt", ios::app);
            if (myfile.is_open())
            {
                myfile << "Card base object constructed\n";
            }
        }
    public:
        virtual ~Card()
        {
            ofstream myfile ("C:/Temp/cardLogFile.txt", ios::app);
            if (myfile.is_open())
            {
                myfile << "Card base object destroyed\n";
                myfile.close();
            }
        //          if (s_numberPlayers > 0)
        //              s_numberPlayers--;
        //          cout<<"Number of cards available is: " << s_numberPlayers << "\n";
        }
        virtual void setRank(U newRank)
        {
            this->itsRank = newRank;
        }
        virtual void setSuit(V newSuit)
        {
            this->itsSuit = newSuit;
        }
        virtual const U getRank()
        {
            return this->itsRank;
        }
        virtual const V getSuit()
        {
            return this->itsSuit;
        }
};


#endif

One card derived class (poker card)

#ifndef POKERCARD_H
#define POKERCARD_H

#include "Card.h"

template <class U, class V>
class PokerCard : virtual public Card <U, V> {
    public:
        PokerCard(): Card <U, V>() {}
        PokerCard(U newRank, V newSuit): Card <U, V>(newRank, newSuit) {}
        ~PokerCard() {}
    protected:
        virtual void setRank(U newRank)
        {
            Card <U, V> ::setRank(newRank);
        }
        virtual void setSuit(V newSuit)
        {
            Card <U, V> ::setSuit(newSuit);
        }
        virtual const U getRank()
        {
            Card <U, V> ::getRank();
        }
        virtual const V getSuit()
        {
            Card <U, V> ::getSuit();
        }
        const int getValue(){return 1;}
};

#endif // POKERCARD_H

The Dealer abstract class (from which the House and players will be derived)

#ifndef DEALER_H
#define DEALER_H

#include <fstream>
#include <cassert>
#include <vector>
#include "PokerCard.h"
#include "TarotCard.h"

template < template <class U, class V> class T>
class Dealer
{
    public:
        Dealer() {}
        Dealer(Card<U, V>* cardType) {}
        virtual ~Dealer() {}
        virtual const vector <Card<U, V>*> getHand()=0;
};

#endif // DEALER_H

And finally, the Dealer derived class (that creates the card pointer to poker card objects)

#ifndef DEALERHOUSE_H
#define DEALERHOUSE_H

#include "Dealer.h"


template < template <class U, class V> class T>
class DealerHouse : virtual public Dealer <T> {
    private:
        vector <Card<U, V>* > dealerDeck;
        Card <U, V> *card;
    public:
        DealerHouse(): Dealer<T>()
        {
            for (int suitInt = Clubs; suitInt != END_OF_SUITS; suitInt++)
            {
                for (int rankInt = Ace; rankInt != END_OF_RANKS; rankInt++)
                {
                    card = new T((U)rankInt, (V)suitInt);
                    if (card != NULL)
                        dealerDeck.push_back(card);
                    else
                        cout <<"ERROR, card object not created in HEAP" << "\n";
                }
            }
            ofstream myfile ("C:/Temp/dealerLogFile.txt", ios::app);
            if (myfile.is_open())
            {
                myfile << "Default Dealer base object constructed:" << "\n";
            }
        }
        DealerHouse(Card<U, V>* cardType): Dealer<T>(cardType)
        {
            dealerDeck.push_back(cardType);
            ofstream myfile ("C:/Temp/dealerLogFile.txt", ios::app);
            if (myfile.is_open())
            {
                myfile << "Parameterized Dealer base object constructed:" << "\n";
            }
        }
        virtual ~DealerHouse()
        {
            if (!dealerDeck.empty())
            {
                dealerDeck.clear();
            }
            if (card != NULL)
            {
                delete card;
                card = NULL;
            }
            ofstream myfile ("C:/Temp/dealerLogFile.txt", ios::app);
            if (myfile.is_open())
            {
                myfile << "Dealer object destroyed:" << "\n";
                myfile.close();
            }
        }
        protected:
        virtual const vector <Card<U, V>*> getHand()
        {
            return dealerDeck;
        }
};

#endif // DEALERHOUSE_H

I'm getting this compile error in Dealer.h, line "Dealer(Card* cardType) {}": CardGame\Dealer.h|16|error: 'U' was not declared in this scope| CardGame\Dealer.h|16|error: 'V' was not declared in this scope|

So I'm guessing I'm screwing the declaration of Dealer.h template arguments "template < template class T> class Dealer", and also the ones in DealerHouse.h, which follow the same syntax.

Any help with that please? I already checked the answers to similar questions Template parameters in C++ templates c++ template to template parameter how to declare template of template class

But when I try the suggested declaration in them, I get the error in DealerHouse.H that the "T" type is not recognized in "card = new T((U)rankInt, (V)suitInt);".

I appreciate any help with this, I'm really stuck with that...

Community
  • 1
  • 1
silvakle
  • 115
  • 10

2 Answers2

1

You don't actually use U,V inside Dealer or DealerHouse, so you don't need the template Card or PokerCard as argument, only a concrete class like Card<U,V>, which is just T. Hence:

template <class T>
class Dealer
{
    // "Card<U,V>" -> "T" everywhere
};

and

template <class T> DealerHouse : public Dealer <T>
{
    // "Card<U,V>" -> "T" everywhere
};

The only point where you're using U,V in DealerHouse is line

card = new T((U)rankInt, (V)suitInt);

which can be just

card = new T(rankInt, suitInt);

if an int can be implicitly converted to U,V (which is the case for a plain enum), or if T has a constructor of the form T(int,int) otherwise.

In case you should definitely need U,V inside e.g. Dealer (or DealerHouse), one way to do this is to provide them as traits inside T, e.g.

template <class U, class V>
class Card
{
    // ...
public:
    using rank = U;
    using suit = V;
};

so that can then "extract" them from T in Dealer like that

using U = typename T::rank;
using V = typename T::suit;

Another way is to specialize Dealer:

template <class T>
class Dealer;

template <template<class, class> class CARD, class U, class V>
class Dealer<CARD<U,V> >
{
    using T = CARD<U,V>;
    // ...
};

at which point you have again all types T,U,V available. This will work for CARD being Card, PokerCard, or anything. This one is closer to your definition, but also includes U,V as template parameters, which you did not, hence not found by your compiler.

A few more points:

  • As far as I can see, you don't need virtual inheritance here, so just say A : public B rather than A : virtual public B.

  • You don't need to redefine Card's virtual methods in derived classes like PokerCard if they're just calling base class Card methods; this is done automatically.

  • Do you really need to protect data behind setter/getter methods?

iavr
  • 7,547
  • 1
  • 18
  • 53
  • Let me clarify some things: - I use virtual inheritance in case I implement a derived Card class that inherits from TarotCard and PokerCard. - I redefined Card's virtual methods because this is the bare bones basis for the app. As I stated, I did the whole thing in Java first, I'm talking card games like BlackJack and Tarot. I need to get the foundations right before implementing all the rest (generics) - I want to protect the data from the players classes (still to be implemented) and automation test suite classes (doing TDD here). – silvakle Mar 05 '14 at 23:21
  • @user3383670 Ok about your clarifications. By the way, you can iterate directly on an `enum`, so you don't need `int` at all. Also, in C++11, you might get used to `nullptr` instead of `NULL`. – iavr Mar 05 '14 at 23:59
  • Got it. Don't have the time to try this until the weekend (up to my ears in work, want to take the time to understand and implement them properly), so will get back with results Monday. – silvakle Mar 06 '14 at 15:04
  • Hey there, Just tried all 3 solutions and the first one worked like a charm!!! It just seemed far more reasonable and consistent to define Card (that is, T) as T(int,int) and have those converted in the constructor to U and V. The other 2 solutions gave me “expected nested-name-specifier” error for "using", so will need to look into that (specialitation without it worked). However I prefer the Int solution, so thanks again!!! – silvakle Mar 10 '14 at 13:43
  • I already voted the reply as useful, however I can't seem to vote up further than that due to me having reputation of 3 (just opened this account, need to have 15 apparently). Guess I need to reply to questions from others myself to up my reputation. – silvakle Mar 11 '14 at 20:18
  • Ok, I see! You also get votes from your questions, e.g. I gave you one here :-) – iavr Mar 11 '14 at 22:21
0

You're misunderstanding what template template parameters are about. You don't need them here. You just want Dealer and DealerHouse to have two type parameters, just like Card.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157