Reputation: 17
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
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);
}
}
Upvotes: 1