Serializing class in QDataStream - c++

I am trying to save 3 QVector<QPoint> and an int with QDataStream.
I overloaded the operator<<(), but if I try to save something, nothing appears in the file.
I checked if the QVector is empty, but it wasn't.
So here is the CloudData class with the operator<< and save() function:
CloudData.h
#include <QDataStream>
#include <QVector>
#include <QPoint>
class CloudData
{
public:
CloudData();
QVector<QPoint> getPolyVector() const;
QVector<QPoint> getPathPointVector() const;
QVector<QPoint> getSupPointVector() const;
int getRandSeed() const;
bool save(QString);
friend QDataStream& operator <<(QDataStream &dataStream, const CloudData &cd)
{
dataStream << cd.getPolyVector() << cd.getPathPointVector()
<< cd.getSupPointVector() << cd.getRandSeed();
return dataStream;
}
private:
QVector<QPoint> m_polyVector;
QVector<QPoint> m_pathPointVector;
QVector<QPoint> m_supPointVector;
int m_randSeed;
};
CloudData.cpp
#include "clouddata.h"
#include <QFile>
CloudData::CloudData()
{
}
void CloudData::setPolyVector(QVector<QPoint> pVec)
{
m_polyVector = pVec;
}
void CloudData::setPathPointVector(QVector<QPoint> ppVec)
{
m_pathPointVector = ppVec;
}
void CloudData::setSuppPointVector(QVector<QPoint> spVec)
{
m_supPointVector = spVec;
}
void CloudData::setRandSeed(int randSeed)
{
m_randSeed = randSeed;
}
bool CloudData::save(QString filename)
{
QFile file(filename);
if(!file.open(QIODevice::WriteOnly))
return false;
QDataStream out(&file);
out.setVersion(QDataStream::Qt_4_8);
out << this;
file.close();
return true;
}
And in the end, here is the function, which calls save():
void CloudWidget::saveCloudData()
{
QString filename = QFileDialog::getSaveFileName(this,tr("Save cloud clutter data"),
"/Users/Admin/Downloads/", "Cloud Clutter Data (*.ccd)");
m_cloudData.setPolyVector(m_polygon);
m_cloudData.setPathPointVector(m_pathPointVector);
m_cloudData.setSuppPointVector(m_supPointVector);
m_cloudData.save(filename);
}

I believe the line:
out << this;
in the save() method should be:
out << *this;
The << operator works on a reference to the class, not a pointer to the class.
The following test code illustrates this:
#include <QFile>
#include <QTextStream>
#include <QDebug>
class StreamTest
{
public:
StreamTest() { var1 = "var1"; var2 = "var2"; }
QString var1;
QString var2;
friend QTextStream& operator <<(QTextStream &textStream, const StreamTest &st)
{
textStream << st.var1 << st.var2;
return textStream;
}
void save(void)
{
QString str_1;
QTextStream text_stream_1(&str_1);
text_stream_1 << this;
qDebug() << str_1;
QString str_2;
QTextStream text_stream_2(&str_2);
text_stream_2 << *this;
qDebug() << str_2;
}
};
int main(int argc, char *argv[])
{
StreamTest st;
st.save();
}
Running the code generates the output:
"0x7fff18070c10"
"var1var2"
this prints the pointer value while *this prints the desired output.

Related

create objects based on entries from a file

How to create objects during runtime based on input from file.
I have few entries in a file in an organized way separated by a space
entryname type satus
entry1 type1 yes
entry2 type2 no
...
I would like to create objects of below class based on number of entries.
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
class Entry{
std::string name;
std::string type;
std::string availability;
};
void getEntriesInfo(std::vector<std::string> &info)
{
std::string line;
std::fstream myfile("entries.txt",std::ios::in);
if (myfile.is_open())
{
//remove first line from file.
getline(myfile,line);
while ( getline(myfile,line) )
info.push_back(line);
myfile.close();
}
else std::cout << "Unable to open file";
}
int main()
{
std::vector<std::string> entries_info;
getEntriesInfo(entries_info);
for(auto &e: entries_info)
std::cout<<e<<std::endl;
return 0;
}
EDIT 1:
I have restructured code a bit using maps and vectors, can it be made more elegant.
If you look at the code , I have not used constructors & destructors properly, (I could not figure out a good way to use), also the setters and not written all kinds of constructors(deep copy or move) etc..
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
//#include <istream>
#include <map>
class AItem;
AItem makeItem(std::string );
void displayItemInfo(std::vector<std::string> info);
class Item {
public:
virtual ~Item() = default;
virtual std::string name() const = 0 ;
virtual std::string quota() const = 0 ;
virtual std::string make() const = 0 ;
};
class AItem : Item{
private:
std:: string _name;
std:: string _quota;
std:: string _make;
public:
virtual std::string name() const { return _name;}
virtual std::string quota() const { return _quota;}
virtual std::string make() const {return _make;}
void setName();
void setQuota();
void setMake();
AItem(){}
AItem(std::string name,std::string quota,std::string make) :_name(name),_quota(quota),_make(make) {}
friend std::istream& operator>>(std::istream& in,AItem& item);
friend std::ostream & operator << (std::ostream &out, const AItem & item);
};
void displayItemInfo(std::vector<std::string> info)
{
for(auto &i_it:info)
std::cout<<i_it<<std::endl;
}
std::vector<std::string> readItemInfo(const char *str)
{
std::string line;
std::vector<std::string> itemInfo;
std::ifstream file(str, std::ios::in);
if(file.is_open()){
getline(file,line);
while(getline(file,line))
itemInfo.push_back(line);
file.close();
}else{
std::cout<<"Unable to open"<<str<<std::endl;
}
return itemInfo;
}
std::ostream &operator << (std::ostream &out,const AItem& item)
{
//std::cout <<item.name()<<" "<<item.quota()<<" "<<item.make() <<std::endl;
return out <<item.name()<<" "<<item.quota()<<" "<<item.make() <<std::endl;
}
std::istream& operator >> (std::istream& in,AItem& item) {
//in >> item._name >> item._quota >> item._make;
return in >> item._name >> item._quota >> item._make;
}
AItem makeItem(std::string aitem)
{
std::stringstream ss(aitem);
std::vector<std::string> arguments;
std::string entry;
while (ss >> entry) {
arguments.push_back(entry);
}
AItem aItem_obj(arguments.at(0),arguments.at(1),arguments.at(2));
return aItem_obj;
}
std::map<std::string,AItem> createItems(std::vector<std::string> item)
{
std::map<std::string,AItem> ItemMap;
for(auto &i_it:item) {
AItem a_item = makeItem(i_it);
ItemMap.insert(std::make_pair(a_item.name(),a_item));
}
// for(auto &m:ItemMap)
// std::cout<<m.first<<m.second<<std::endl;
return ItemMap;
}
int main(int argc, char*argv[])
{
char *str = "player_info.txt";
std::vector<std::string> info;
std::map<std::string,AItem> ItemMap;
// info = readItemInfo(str);
// displayItemInfo(info);
//#if 0
ItemMap = createItems(readItemInfo(str));
for(auto &item:ItemMap)
std::cout<<item.first<<item.second<<std::endl;
//#endif
return 0;
}
EDIT 2
I have done this way
entry.h
#include <iostream>
#include <string>
class Entry{
private:
std::string name;
std::string type;
std::string availability;
public:
std::string getName(void) const;
std::string getType(void) const;
std::string getAvailability(void) const;
void setName(const std::string n);
void setType(const std::string t);
void setAvailability(const std::string y);
Entry();
~Entry();
friend std::ostream &operator << (std::ostream &out,const Entry &e);
};
entry.cpp
#include "entry.h"
std::string Entry ::getName(void) const{
return name;
}
std::string Entry ::getType(void) const{
return type;
}
std::string Entry ::getAvailability(void) const{
return availability;
}
void Entry ::setName(const std::string n){
name = n;
}
void Entry ::setType(const std::string t){
type = t;
}
void Entry ::setAvailability(const std::string y){
availability = y;
}
Entry::Entry(){
}
Entry::~Entry(){
}
std::ostream &operator << (std::ostream &out,const Entry &e){
return out << e.getName()<<" " <<e.getType()<<" "<<e.getAvailability()<<std::endl;
}
main.cpp
#include <fstream>
#include <vector>
#include "entry.h"
void makeEntry(std::string line,std::vector<Entry> &entries)
{
Entry P1;
P1.setName(line.substr(0, line.find(' ')));
P1.setType(line.substr(P1.getName().size(), line.find(' ')));
P1.setAvailability(line.substr(P1.getName().size()+P1.getType().size(), line.find(' ')));
//std::cout<<"C:"<<P1.getName()<<P1.getType()<<P1.getAvailability()<<std::endl;
entries.push_back(P1);
}
void getEntriesInfo(std::vector<std::string> &info)
{
std::string line;
std::fstream myfile("entries.txt",std::ios::in);
if (myfile.is_open())
{
//remove first line from file.
getline(myfile,line);
while ( getline(myfile,line) )
info.push_back(line);
myfile.close();
}
else std::cout << "Unable to open file";
}
int main()
{
std::vector<std::string> entries_info;
std::vector<Entry> entries;
getEntriesInfo(entries_info);
for(auto &e: entries_info) {
std::cout<<e<<std::endl;
makeEntry(e,entries);
}
std::cout<<"OUT"<<std::endl;
for(auto &e: entries) {
std::cout<<e<<std::endl;
}
return 0;
}
You can provide an overload for the stream extraction operator:
std::istream& operator>>(std::istream& in,Entry& e) {
in >> e.name >> e.type >> e.availability;
}
This works, when the individual entries in the file do not contain spaces. Ie it would fail for
entryName LastName type1 yes
In this case you would need to have a seperator (eg ;) and use std::getline to parse the lines.
Reading entries into a vector is then (assuming you know the number of entries beforehand):
std::vector<Entry> readFromFile(std::istream& myfile,size_t n_entries) {
std::vector<Entry> entries;
data.resize( n_entries );
for (auto& e : entries ) myfile >> e;
return entries;
}

I am trying to make a logging framework in c++ but information is not being passed to the logger's subcomponents, what am i doing wrong?

here is my code, when i try running this, main does output the information placed into the LoggerComponent, but not the Logger itself. I don't know why, what could be preventing the logger from passing information into the underlying loggercomponent?
i tried using information from http://www.cplusplus.com/reference/map/map/
and from https://www.geeksforgeeks.org/map-associative-containers-the-c-standard-template-library-stl/
logger.cpp:
#include "logger.hpp"
Logger::Logger(bool verbose, bool fileoutput)
{
if(verbose)
{
LoggerComponent c1(LoggerLevel::DEBUG, &std::cout);
addLogger (LoggerType::CONSOLE, &c1);
c1.output (LoggerLevel::DEBUG, "This is the start of console output");
}
if(fileoutput)
{
}
}
void Logger::output(LoggerLevel level, std::string message)
{
for(auto& x : components)
{
x.second->output (level, message);
}
}
void Logger::addLogger(LoggerType type, LoggerComponent* component)
{
if(components.find (type) == components.end ())
components.emplace(type, component);
}
LoggerComponent* Logger::getLogger (LoggerType type)
{
if(components.find (type) != components.end ())
return components.at (type);
return nullptr;
}
void Logger::clearLoggers()
{
components.clear ();
}
void Logger::removeLogger(LoggerType type)
{
if(components.find (type) != components.end ())
components.erase (type);
}
logger.hpp
#ifndef LOGGER_HPP
#define LOGGER_HPP
#include "loggercomponent.hpp"
#include <map>
enum class LoggerType
{
CONSOLE,
FILE
};
class Logger
{
public:
explicit Logger(bool verbose, bool fileoutput);
void output(LoggerLevel level, std::string message);
void addLogger(LoggerType type, LoggerComponent* component);
void removeLogger(LoggerType type);
void clearLoggers();
LoggerComponent* getLogger(LoggerType type);
private:
std::map<LoggerType, LoggerComponent*> components;
};
#endif // LOGGER_HPP
main.cpp
#include "logger.hpp"
int main()
{
int* p;
int i = 5;
int j = 5;
p = &i;
std::cout << p << std::endl;
p = &j;
std::cout << p << std::endl;
LoggerComponent c(LoggerLevel::DEBUG, &std::cout);
c.output (LoggerLevel::INFO, "Hello World!");
c.output (LoggerLevel::CRITICAL, "Hello World!");
Logger c2(true, true);
std::cout << c.getOutputStream () << std::endl;
std::cout << c2.getLogger (LoggerType::CONSOLE)->getOutputStream () << std::endl;
c2.output (LoggerLevel::INFO, "Hello World!");
c2.output (LoggerLevel::CRITICAL, "Hello World!");
}
loggercomponent.hpp
#ifndef LOGGERCOMPONENT_HPP
#define LOGGERCOMPONENT_HPP
#include <iostream>
#include <string>
#include <ctime>
enum class LoggerLevel
{
INFO,
DEBUG,
WARNING,
ERROR,
CRITICAL
};
class LoggerComponent
{
public:
explicit LoggerComponent(LoggerLevel level, std::ostream* output);
LoggerLevel getMinimumLevel();
std::ostream* getOutputStream();
void setMinimumLevel(LoggerLevel level);
void setOutputStream(std::ostream* output);
void output(LoggerLevel level, std::string outputMessage);
private:
std::string getLevelString(LoggerLevel level);
LoggerLevel minimumLevel;
std::ostream* outputStream;
};
#endif // LOGGERCOMPONENT_HPP
loggercomponent.cpp
#include "loggercomponent.hpp"
LoggerComponent::LoggerComponent(LoggerLevel level,
std::ostream* output)
{
setMinimumLevel (level);
setOutputStream (output);
}
void LoggerComponent::setMinimumLevel(LoggerLevel level)
{
if(minimumLevel != level)
minimumLevel = level;
}
void LoggerComponent::setOutputStream(std::ostream *output)
{
if(outputStream != output)
outputStream = output;
}
LoggerLevel LoggerComponent::getMinimumLevel()
{
return minimumLevel;
}
std::ostream* LoggerComponent::getOutputStream()
{
return outputStream;
}
std::string LoggerComponent::getLevelString(LoggerLevel level)
{
switch (level) {
case LoggerLevel::INFO:
return "INFO";
case LoggerLevel::DEBUG:
return "DEBUG";
case LoggerLevel::WARNING:
return "WARNING";
case LoggerLevel::ERROR:
return "ERROR";
case LoggerLevel::CRITICAL:
return "CRITICAL";
}
return nullptr;
}
void LoggerComponent::output(LoggerLevel level, std::string outputMessage)
{
if(level >= minimumLevel)
{
time_t now = time(nullptr);
*outputStream << ctime(&now)
<< (getLevelString (level) + " >> " + outputMessage)
<< std::endl << std::endl;
}
}
output:
0x60fda8
0x60fdac
Tue Oct 01 12:29:14 2019
CRITICAL >> Hello World!
Tue Oct 01 12:29:14 2019
DEBUG >> This is the start of console output
0x6fd0cd00
0x60fdb0
You are storing a pointer to an object local to the constructor (c1) in components. It will be destroyed and the pointer invalid when you try to use it later.
Store the object itself (or a std::unique_ptr to it if you have a good reason not to store the object itself) in the map instead.

How to run a function of an actual object, which is stored in the container using OOP?

How to run a function of an actual object, which is stored in the container using OOP?
Background: I'm writing a game. There is a set of 4 interconnected rooms. There are two different room types and two different player types. Players should run as threads. The Killer should have a fight with a normal player in the Action room. In the second type of room, nothing should happen. The game logic and code is simplified.
When the thread starts, void Player::operator()() is being executed. The player enters the room, does his action initializeAction(), and leaves it. In case of a Killer, his initializeAction() leads to room->actionInRoom(*this), which executes player.inActionRoom().
The problem is in this code void Killer::inActionRoom():
std::vector<Player> &playersWithoutKillers = room->getPlayersWithoutKillers();
auto it = playersWithoutKillers.begin();
std::advance(it, 0);
Player chosenPlayerForFight = *it;
...
chosenPlayerForFight.decreasePoints();
where chosenPlayerForFight.decreasePoints(); does not decrease the points for the actual player, but I think it does it for a copy of an object.
If I run the code, this mistake is visible: OtherPlayer's points will always reset to 1. If I'm decreasing it every time the fight occurs, the negative value is expected.
-> Killer in Forth room Killer 11 vs OtherPlayer 1
Starting decreasing points from 1
Ending decreasing points from 0
I tried to fix the code, mainly by making sure the reference of an object is passed.
Main.cpp:
#include <iostream>
#include <memory>
#include <thread>
#include "Room.h"
#include "Player.h"
std::mutex globalMessageMutex;
int main() {
auto first = std::make_shared<RelaxRoom>("First room");
auto second = std::make_shared<ActionRoom>("Second room");
auto third = std::make_shared<RelaxRoom>("Third room");
auto forth = std::make_shared<ActionRoom>("Forth room");
first->setRoomPair(second, forth);
second->setRoomPair(third, first);
third->setRoomPair(forth, second);
forth->setRoomPair(first, third);
std::vector<std::thread> players;
players.emplace_back(OtherPlayer("OtherPlayer", first));
players.emplace_back(Killer("Killer", first));
for (auto &t : players) {
if (t.joinable()) {
t.join();
}
}
return 0;
}
Player.h
#ifndef HW03_PLAYER_H
#define HW03_PLAYER_H
#include <string>
#include <memory>
class Room;
class Player {
friend class Room;
public:
Player(const std::string &playerName, std::shared_ptr<Room> initialTargetRoom);
void operator()();
friend bool operator== ( const Player &lhs, const Player &rhs );
const std::string &getName() const;
virtual void inActionRoom() {};
virtual void inRelaxRoom(Room &pRoom) {};
virtual bool isKiller()const;
virtual bool isOtherPlayer();
int getPoints()const;
void increasePoints();
void decreasePoints();
int points;
protected:
std::shared_ptr<Room> room;
virtual void initializeAction();
private:
std::string name;
std::shared_ptr<Room> initialRoom;
};
class OtherPlayer : public Player {
public:
OtherPlayer(const std::string &playerName, const std::shared_ptr<Room> &initialTargetRoom);
void initializeAction() override;
void inActionRoom() override;
void inRelaxRoom(Room &pRoom) override;
bool isOtherPlayer() override;
};
class Killer : public Player {
public:
Killer(const std::string &playerName, const std::shared_ptr<Room> &initialTargetRoom);
void initializeAction() override;
void inActionRoom() override;
void inRelaxRoom(Room &pRoom) override;
bool isKiller() const override;
};
#endif //HW03_PLAYER_H
Player.cpp
#include <iostream>
#include <chrono>
#include <thread>
#include <random>
#include "Player.h"
#include "Room.h"
extern std::mutex globalMessageMutex;
Player::Player(const std::string &playerName, std::shared_ptr<Room> initialTargetRoom) {
name = playerName;
initialRoom = initialTargetRoom;
room = initialTargetRoom;
points = 1;
}
void Player::operator()() {
room->enter(*this);
initializeAction();
std::this_thread::sleep_for(std::chrono::milliseconds(20));
room->leave(*this);
while (auto nextRoom = room->getNext()) {
room = nextRoom;
nextRoom->enter(*this);
initializeAction();
std::this_thread::sleep_for(std::chrono::milliseconds(20));
nextRoom->leave(*this);
}
}
void Player::initializeAction() {}
bool operator==(const Player &lhs, const Player &rhs) {
return lhs.name == rhs.name;
}
const std::string &Player::getName() const {
return name;
}
bool Player::isKiller() const {
return false;
}
bool Player::isOtherPlayer() {
return false;
}
int Player::getPoints() const {
return points;
}
void Player::increasePoints() {
points++;
}
void Player::decreasePoints() {
std::cout << "Starting decreasing points from " << points << std::endl;
points--;
std::cout << "Ending decreasing points from " << points << std::endl;
}
OtherPlayer::OtherPlayer(const std::string &playerName, const std::shared_ptr<Room> &initialTargetRoom) : Player(playerName,
initialTargetRoom) {}
void OtherPlayer::initializeAction() {
room->actionInRoom(*this);
}
void OtherPlayer::inActionRoom() {}
void OtherPlayer::inRelaxRoom(Room &pRoom) {}
bool OtherPlayer::isOtherPlayer() {
return true;
}
Killer::Killer(const std::string &playerName, const std::shared_ptr<Room> &initialTargetRoom) : Player(playerName,
initialTargetRoom) {}
void Killer::initializeAction() {
room->actionInRoom(*this);
}
void Killer::inActionRoom() {
std::lock_guard<std::mutex> ml(globalMessageMutex);
if (!room->getPlayersWithoutKillers().empty()) {
**std::vector<Player> &playersWithoutKillers = room->getPlayersWithoutKillers();**
**auto it = playersWithoutKillers.begin();
std::advance(it, 0);
Player chosenPlayerForFight = *it;**
auto killersVitality = this->getPoints();
auto othersPlayerPoints = chosenPlayerForFight.getPoints();
std::cout << "-> Killer in " << room->getName() << " " << this->getName() << " " << killersVitality
<< " vs " << chosenPlayerForFight.getName() << " " << othersPlayerPoints << std::endl;
this->increasePoints();
**chosenPlayerForFight.decreasePoints();**
}
}
void Killer::inRelaxRoom(Room &pRoom) {
}
bool Killer::isKiller() const {
return true;
}
Room.h
#ifndef HW03_ROOM_H
#define HW03_ROOM_H
#include <string>
#include <vector>
#include <condition_variable>
class Player;
class Room {
public:
Room(const std::string &roomName);
void setRoomPair(std::shared_ptr<Room> firstRoom, std::shared_ptr<Room> secondRoom);
std::shared_ptr<Room> getNext();
void enter(Player &player);
void leave(Player &player);
virtual void actionInRoom(Player &player)= 0;
const std::string &getName() const;
const std::vector<Player> &getPlayers();
std::vector<Player> &getPlayersWithoutKillers();
protected:
std::string name;
size_t killersCount;
size_t playersWithoutKillersCount;
private:
std::vector<Player> players;
std::vector<Player> playersWithoutKillers;
std::condition_variable cv;
std::mutex mutex;
std::pair<std::shared_ptr<Room>, std::shared_ptr<Room>> roomPair;
void updateCounterPlayerLeaves(Player &player);
void updateCounterPlayerEnters(Player &player);
};
class ActionRoom : public Room {
public:
ActionRoom(const std::string &roomName) : Room(roomName) {}
void actionInRoom(Player &player) override;
};
class RelaxRoom : public Room {
public:
RelaxRoom(const std::string &roomName) : Room(roomName) {}
void actionInRoom(Player &player) override;
};
#endif //HW03_ROOM_H
Room.cpp
#include <iostream>
#include <algorithm>
#include <random>
#include "Room.h"
#include "Player.h"
#include <mutex>
extern std::mutex globalMessageMutex;
Room::Room(const std::string &roomName) {
name = roomName;
}
const std::string &Room::getName() const {
return name;
}
std::shared_ptr<Room> Room::getNext() {
auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
std::mt19937 engine(seed);
std::uniform_int_distribution<int> randomGenerator(0, 1);
auto randomNumber = randomGenerator(engine);
if (randomNumber) {
return roomPair.second;
}
return roomPair.first;
}
void Room::enter(Player &player) {
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [this, &player] {
return true;
}
);
players.push_back(player);
updateCounterPlayerEnters(player);
std::lock_guard<std::mutex> ml(globalMessageMutex);
std::cout << name << ": killers: " << killersCount << ", other players: " << playersWithoutKillersCount <<
std::endl;
}
void Room::updateCounterPlayerEnters(Player &player) {
if (player.isKiller()) {
killersCount++;
} else {
playersWithoutKillersCount++;
playersWithoutKillers.push_back(player);
}
}
void Room::leave(Player &player) {
{
std::lock_guard<std::mutex> lock(mutex);
auto it = std::find(players.begin(), players.end(), player);
if (it == players.end()) {
return;
}
players.erase(it);
updateCounterPlayerLeaves(player);
}
cv.notify_all();
}
void Room::updateCounterPlayerLeaves(Player &player) {
if (player.isKiller()) {
killersCount--;
} else {
playersWithoutKillersCount--;
auto it = std::find(playersWithoutKillers.begin(), playersWithoutKillers.end(), player);
if (it == playersWithoutKillers.end()) {
return;
}
playersWithoutKillers.erase(it);
}
}
void Room::setRoomPair(std::shared_ptr<Room> firstRoom, std::shared_ptr<Room> secondRoom) {
roomPair.first = std::move(firstRoom);
roomPair.second = std::move(secondRoom);
}
const std::vector<Player> &Room::getPlayers() {
return players;
}
std::vector<Player> &Room::getPlayersWithoutKillers() {
return playersWithoutKillers;
}
void ActionRoom::actionInRoom(Player &player) {
player.inActionRoom();
}
void RelaxRoom::actionInRoom(Player &player) {
player.inRelaxRoom(*this);
}
Not a complete answer, since you said this is a homework assignment.
However, one pattern you can use to solve this problem is to have your container store std::unique_ptr<some_base_class> values, or if necessary std::shared_ptr<some_base_class>, and fill them with pointers to derived objects. For example: container.emplace_back(static_cast<some_base_class*>(new derived_class(foo, bar, baz))); will construct everything in place inside your container, so the compiler doesn’t need to make any temporary copies. You might also write something like:
std::vector<std::unique_ptr<some_base_class>> container;
{
std::unique_ptr<derived_class> temp =
make_unique<derived_class>();
temp->setup( foo, bar, baz );
container.emplace_back(static_cast<some_base_class*>(temp.release()));
// temp is now empty, and the pointer in the container now owns the object.
}
You can use the full interface of some_base_class through these smart pointers, with ->, but if you need to turn them back into references to derived objects, you would use RTTI and dynamic_cast.
There are other approaches, including a discriminated union and std::variant, but storing smart pointers to the base class that defines your interface is what I recommend you try here.

c++ How can I access the Vector Data created by Composition?

I'll go straight to an example, I think it is easier to underestand.
Music Cd has Tracks. How can I access A TrackInfo vector (XTrackInfo) data "inside" Music Cd class?
I want to print and even change values, I don't figure out how.
Thanks
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <numeric>
class XTrackInfo
{
std::string m_TrackName;
int m_Length;
public:
XTrackInfo() {}
XTrackInfo(std::string TrackName, int Length):
m_TrackName(std::move(TrackName)),
m_Length(Length)
{}
void SetTrackName(std::string TrackName) { m_TrackName = std::move(TrackName); }
void SetTrackLength(int Length) { m_Length = Length; }
const std::string& GetTrackName() const { return m_TrackName; }
int GetTrackLength() const { return m_Length; }
};
class XMusicCd
{
private:
std::string m_Author;
std::vector<XTrackInfo> m_TrackInfo;
public:
XMusicCd() {}
XMusicCd(std::string Author, std::vector<XTrackInfo> Tracks):
m_Author(std::move(Author)),
m_TrackInfo(std::move(Tracks))
{}
void SetAuthor(std::string Author) { m_Author = std::move(Author); }
const std::string& GetAuthor() const { return m_Author; }
const std::vector<XTrackInfo> GetTracks() const { return m_TrackInfo;}
int GetLength() const; // Left incomplete on purpose; you will implement it later
void AddTrack(XTrackInfo NewTrack){
m_TrackInfo.emplace_back(std::move(NewTrack));
}
};
void PrintCdContents(const XMusicCd& Cd)
{
std::cout << "Author : " << Cd.GetAuthor() << "\n";
std::cout << "\n" << std::endl;
std::cout << "Track Info" << std::endl;
//problems here :)
}
int main()
{
// You may not change this function
XMusicCd MyCd;
MyCd.SetAuthor("Hello World");
MyCd.AddTrack(XTrackInfo("This is a test", 100));
MyCd.AddTrack(XTrackInfo("This is a test 2", 200));
PrintCdContents(MyCd);
}
Use iterators:
std::vector<XTrackInfo> tracks = Cd.GetTracks();
for (std::vector<XTrackInfo>::const_iterator it = tracks.begin(); it != tracks.end(); ++it) {
std::cout << it->GetTrackName() << std::endl;
}
Or indexes:
std::vector<XTrackInfo> tracks = Cd.GetTracks();
for (unsigned i = 0; i < tracks.size(); ++i) {
std::cout << tracks.at(i).GetTrackName() << std::endl;
}

Redirect and timestamp std::cout

I need to redirect an ofstream to a file and timestamp every line that's printed.
(It's part of a logging system).
I have a working class that manages to do just that but it refuses to flush the file when std::endl is emmited. I'd apreciate any help on that.
(If there is a simpler way to do this do tell).
#include <iostream>
#include <streambuf>
#include <fstream>
#include <sys/time.h>
#include <cstring>
#include <memory>
class TimeStampBuf: public std::streambuf {
public:
explicit TimeStampBuf(std::streambuf* dest) :
_dest(dest),
_isAtStartOfLine(true),
_owner( NULL) {
}
explicit TimeStampBuf(std::ostream& dest) :
_dest(dest.rdbuf()),
_isAtStartOfLine(true),
_owner(&dest) {
_owner->rdbuf(this);
}
virtual ~TimeStampBuf() {
if (_owner != NULL) {
_owner->rdbuf(_dest);
}
}
protected:
virtual int overflow(int ch) {
if (_isAtStartOfLine) {
char timebuff[30];
timeval curTime;
gettimeofday(&curTime, NULL);
strftime(timebuff, sizeof(timebuff), "%Y-%m-%d %H:%M:%S:",
localtime(&curTime.tv_sec));
sprintf(timebuff + strlen(timebuff), "%03u\t",
(unsigned int) curTime.tv_usec / 1000);
_dest->sputn(timebuff, strlen(timebuff));
}
_isAtStartOfLine = ch == '\n';
return _dest->sputc(ch);
}
private:
std::streambuf *_dest;
bool _isAtStartOfLine;
std::ostream *_owner;
};
class OutputRedirectAndStamp {
public:
OutputRedirectAndStamp(std::string file, std::ostream &s = std::cout, std::ios::openmode mode = std::ios::out){
_s=&s;
_file=file;
if(_file.size()){
_mode=mode;
_buf.open(file.c_str(),mode);
_coutBuf = s.rdbuf((std::streambuf*)&_buf);
}
_tsb.reset(new TimeStampBuf(s));
}
void reopen(void){
_tsb.reset();
if(_file.size()){
_s->rdbuf(_coutBuf); //reset to previous output
_buf.close();
_buf.open(_file.c_str(),_mode);
_coutBuf = _s->rdbuf((std::streambuf*)&_buf);
}
_tsb.reset(new TimeStampBuf(*_s));
}
~OutputRedirectAndStamp() {
_tsb.reset();
if(_file.size()){
_s->rdbuf(_coutBuf); //reset to previous output
}
}
private:
std::string _file;
std::ios::openmode _mode;
std::ostream *_s;
std::filebuf _buf;
std::streambuf *_coutBuf;
std::unique_ptr<TimeStampBuf> _tsb;
};
int main() //example main
{
std::unique_ptr<OutputRedirectAndStamp> a;
a.reset(new OutputRedirectAndStamp("test.txt",std::cout,std::ios::app | std::ios::out));
std::cout<<"this is written to file"<<2<<std::endl;
a->reopen();
std::cout<<"this is written to file also"<<std::endl;
a.reset();
std::cout<<"this is written to stdout"<<std::endl;
return 0;
}
When you flush an std::ostream, the stream buffer's pubsync() is called which in turn calls the virtual function sync(). If you don't override sync() it will just claim that it succeeded by returning 0. From your sync() override you should just call the held stream buffer's pubsync():
int TimeStampBuf::sync() {
return _dest->pubsync();
}