U Kushi
U Kushi

Reputation: 17

Overloading operator<< twice in same class for different member variables

Sorry if this question has been asked before, but I'm struggling with overloading the << operator to stream different data into multiple files.

I have a Player class, which has the following attributes:

char* name;
char* password;
int hScore;
int totalGames;
int totalScore;
int avgScore;

I want to overload the << operator twice: one to stream the name, password and hScore to a "Players.txt" file, and a second overload to stream the totalGames, totalScore and avgScore to a different .txt file which is based off each player's name, e.g. "Player1.txt".

Here's what my operator looks like in the Player class:

friend ostream& operator<< (std::ostream& os, Player& player)
{
    os << player.name << "\n" << player.encryptPassword((player.password), 3) << "\n" << player.hScore << "\n";
    return os;
}

And here's where I am calling it, from a PlayerLibrary class which contains a vector of Players:

ofstream out("Yahtzee.txt");
if (out.is_open())
{
    for_each(playerList.begin(), playerList.end(), [&out](Player* player) {out << (*player);});
}
else
{
    cout << "THERE WAS AN ERROR WRITING TO FILE\n";
}

Basically, I want to stream the other variables into another file which is named after the player name, and contains a scorecard for each game they've played. So far it looks like:

for (auto it = playerList.begin(); it != playerList.end(); ++it)
{
    auto position = it - playerList.begin();
    string filename(playerList[position]->getName());
    filename = filename + ".txt";
    ofstream out2(filename);

    for (int i = 0; i < playerList[position]->getNumberOfScorecards(); i++)
    {
        out2 << *playerList[position]->getScorecard(i);
    }
}

This only streams the scorecard and not the totalGames, totalScore and avgScore, like I want it to.

I have tried just moving those variables into the scorecard class, but I feel that it makes more sense to have them where they are.

I understand that I can't overload operator<< twice if both overloads have the same parameters, is there another way of going about this? Is there anyway perhaps in the overloaded function to use the output stream and check the name of the .txt file or something.

Hope the question makes sense.

Upvotes: 0

Views: 256

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 598114

Rather than defining an operator<< for Player itself, create a couple of utility types that refer to the Player and have their own operator<<s, let them decide which portions of the Player to stream, eg:

class Player
{
private:
    std::string name;
    std::string password;
    int hScore;
    int totalGames;
    int totalScore;
    int avgScore;
    ...

public:
    ...

    std::string getName{} const { return name; }
    ...

    std::string EncryptPassword(int arg) const { return ...; }

    int getNumberOfScorecards() const { return ...; }
    Scorecard* getScorecard(int index) const { return ...; }

    class Info
    {
        const Player &m_player;

        void print(std::ostream &os) const {
            os << m_player.name << "\n" << m_player.encryptPassword(3) << "\n" << m_player.hScore << "\n";
        }

    public:
        Info(const Player &player) : m_player(player) {}

        friend std::ostream& operator<<(std::ostream &os, const Info &info)
        {
            info.print(os);
            return os;
        }
    };
    friend class Info;

    struct Stats
    {
        const Player &m_player;

        void print(std::ostream &os) const
        {
            os << m_player.totalGames << "\n" << m_player.totalScore << "\n" << m_player.avgScore << "\n";
        }

    public:
        Stats(const Player &player) : m_player(player) {}

        friend std::ostream& operator<<(std::ostream &os, const Stats &stats)
        {
            stats.print(os);
            return os;
        }
    };
    friend class Stats;
};

And then you can use them like this:

ofstream out("Yahtzee.txt");
if (out.is_open())
{
    for(auto *player : playerList)
        out << Player::Info(*player);
}
else
{
    cout << "THERE WAS AN ERROR WRITING TO FILE\n";
}
for (auto *player : playerList)
{
    ofstream out2(player->getName() + ".txt");
    out2 << Player::Stats(*player);

    for (int i = 0; i < player->getNumberOfScorecards(); ++i)
    {
        out2 << *player->getScorecard(i);
    }
}

Online Demo

Upvotes: 1

Related Questions