How to iterate over a vector containing many stucts (C++) - c++

// In my Class A, I have many nodes and every node data is stored in a struct like this:
Class A
{
private:
struct BriteNodeInfo
{
int nodeId;
double xCoordinate;
double yCoordinate;
int inDegree;
int outDegree;
int asId;
std::string type;
};
};
// Each node instance is stored in a vector like this:
typedef std::vector<BriteNodeInfo> BriteNodeInfoList;
BriteNodeInfoList m_briteNodeInfoList;
//And then, here is the function that I want to implent down below
void SaveNodeData (std::string fname);
};
Problem: How do I implent that SaveNodeData() function to save my nodes data in .txt file like this?:
nodeId0 yCoordinate0 xCoordinate0
nodeId1 yCoordinate1 xCoordinate1
nodeId2 yCoordinate2 xCoordinate2
nodeId3 yCoordinate3 xCoordinate3
etc...
I have tried but my iteration syntax is not good enough. Here is my function, please help:
Here is my failed function:
void SaveNodeData (std::string fname)
{
ofstream os(fname.c_str(), ios::trunc);
vector<BriteNodeInfo> BriteNodeInfoList;
BriteNodeInfoList m_briteNodeInfoList;
for (BriteNodeInfoList::Iterator i = m_briteNodeInfoList.Begin(); i != m_briteNodeInfoList.End(); ++i)
{
os << BriteNodeInfo[i].nodeId "\t" << "\t" << BriteNodeInfo[i].yCoordinate; << "\t"BriteNodeInfo[i].xCoordinate<< "\n";
}
os << "\n";
}

Before starting, this code as written will clearly have some compile errors. But assuming you can manage to fix those issues, there's one huge flaw.
Your function SaveNodeData creates an empty BriteNodeInfoList, and then tries to read from it. The for loop you have written will always simply exit.
What you need to do is create and populate a BriteNodeInfoList somewhere that this function reads. You could pass it in as an argument to the function, have it as a private variable for class A (assuming SaveNodeData is made into a member of class A). Or you could make it a static member variable of class A (not really recommended: static member variables of objects have some serious problems).

void SaveNodeData (std::string fname) // file path
{
ofstream os(fname.c_str(), ios::trunc);
vector<BriteNodeInfo> BriteNodeInfoList = m_briteNodeInfoList;
for (std::vector<BriteNodeInfoList>::Iterator it = BriteNodeInfoList.Begin(); it != BriteNodeInfoList.End(); ++it)
{
os << (*it).nodeId << "\t" << (*it).yCoordinate << "\t" << (*it).xCoordinate << "\n";
}
}

Related

Is it possible to bind a function from a vector of objects to a std::function, or how would you access a function from a vector of objects this way

I have a two classes, one of which is a 'main' configuration, the other is a secondary configuration with a subset of some options from the main configuration. One of the 'main' configuration members is a vector of secondary configuration objects used to override the main configuration options if present. I can bind the 'main' configurations get methods to functions which I add to a vector and use to get those parameters in testing (not all options are tested every time, so I create a vector of set values which matches the vector of get methods which I compare.)
I would like to similarly bind the get methods from the secondary configurations, specifically the last (should be only) one in the vector, but I am unsure how to do this or if it is even possible. I have tried a number of tweaks that make sense to me, however I am starting to doubt if this would even be a good idea with the amount of convolution I am creating. If anyone could point me to how I would accomplish this, either by binding it in such a way that it calls the desired method from the first object in the vector or another way, that is what I am looking for. A heavily paired down example of what I am attempting to do is below.
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
class secConfig{
private:
double data;
public:
secConfig(double);
double get_data();
};
secConfig::secConfig(double indata){
data = indata;
}
double secConfig::get_data(){
return data;
}
class config{
private:
std::vector<secConfig> secConfigs;
double normalData;
public:
config(double, vector<secConfig>);
double getData();
vector<secConfig> getVecData();
};
config::config(double inData, vector<secConfig> inVec){
normalData = inData;
secConfigs = inVec;
}
double config::getData(){
return normalData;
}
vector<secConfig> config::getVecData(){
return secConfigs;
}
int main(int argc, char const *argv[])
{
secConfig initial(66.6);
vector<secConfig> inConfigs;
inConfigs.push_back(initial);
cout << "Inconfig raw data: " << initial.get_data() << endl;
cout << "Inconfig vector data: " << inConfigs.back().get_data() << endl;
config mainConfig(55.5, inConfigs);
cout << "Main config raw data: " << mainConfig.getData() << endl;
cout << "Main config internal vec data: " << mainConfig.getVecData().back().get_data() << endl;
vector<function<double()>> getvals;
getvals.push_back(bind(&config::getData, &mainConfig));
cout << "Main config bound data: " << getvals[0]() << endl;
// Something like: getvals.push_back(bind(&config::getVecData::back::get_data, &mainConfig));
// So I can: cout << "Secondary (vectorized) config data : " << getvals[1]() << endl;
return 0;
}
std::bind is rarely the best or easiest way to achieve something.
Use a lambda instead.
getvals.push_back( [&]{ return mainConfig.getVecData().back().get_data(); } );

C++: Finding the highest value of a class within a linked list

I'm working on an assignment that involves creating a linked list of applicants (stored as a class), each having a value for ID, grade, years, and a numerical value (score). I need to write a function that searches this list for the applicant with the highest score and returns it. I was able to locate the highest score, but I would only return that value, and not the entire class of that specific applicant. After changing my code, I now get outputs for each of the 4 values (ID, grade, years, and score) but they all show 0, which is the initialized value. Here is all the relevant code.
#include <iostream>
#include <fstream>
#include <iomanip>
#include "applicant.cpp"
using namespace std;
struct Linkapp
{
Applicant person;
Linkapp *next;
};
class Linkthem
{
protected:
Linkapp *start;
public:
Linkthem(void);
void link(Applicant);
void printthem(void);
Applicant returnbest(void);
};
Applicant best;
Linkthem::Linkthem()
{
start = NULL;
};
void
Linkthem::link(Applicant one)
{
Linkapp *p;
p = new Linkapp;
p->person = one;
p->next = start;
start = p;
}
Applicant Linkthem::returnbest (void)
{
Linkapp *travel;
travel = start;
best = travel->person;
while (travel != NULL)
{
if (travel->person.return_value() > best.return_value())
best = travel->person;
travel = travel->next;
}
return best;
}
int
main()
{
ifstream infile;
Applicant fellow;
Linkthem mylist;
int id, yrs;
char knowledge;
cout.setf(ios::fixed);
cout.precision(2);
infile.open("applicnt.dat");
while ( !infile.eof() )
{
infile >> id >> knowledge >> yrs;
fellow.store_id(id);
fellow.store_skill(knowledge);
fellow.store_years(yrs);
mylist.link(fellow);
}
mylist.printthem();
cout << best.return_id() << ' ' << best.return_skill() << ' ';
cout << best.return_years() << ' ' << best.return_value() << endl;
fellow = mylist.returnbest();
return 0;
}
And from the "applicant.cpp"
class Applicant
{
protected:
int id;
char skill;
int years;
float value;
void calc_value(void); // <--- NOT for general use, called by return_value
public:
Applicant(void);
void store_id(int);
void store_skill(char);
void store_years(int);
int return_id(void);
char return_skill(void);
int return_years(void);
float return_value(void);
};
The problem definitely lies within my returnbest function, I'm just not sure where.
Just used std::max_element and std::list (which is doubly linked) or std::forward_list (which is singly linked), but I would probably default to std::vector instead:
std::max_element(begin(list), end(list), [](Applicant const& a, Applicant const& b) {
return (a.return_value() < b.return_value());
});
If the comparison function is intended for the entire Applicant class, it might make sense to define an operator< for said class.
As you seem to have to write your own list, some hints:
Linkapp appears to be an internal detail of your class Linkthem, so no bad idea to make it a private inner class.
returnbest (and probably printthem, if I may assume analogous implementation) will fail with segmentation fault, if your list is empty. You need to check, if start is null first:
if(start)
{
// rest of your code
}
else
{
// return some appropriate default
// throw exception
// or whatever else seems appropriate to you
}
You have quite uncommon names for your getters/setters. There are two main variants for naming:
int getId(); // or get_id(), but cammel casing is more common these days
void setId(int id);
// vs
int id();
void id(int id);
There is no need for Applicant best to be a global variable. Rather place it within the functions as local variables.
Now your actual problem:
Keep a close look on your main function - where do you call returnbest first? After you wrote out the values of best. This means that you never assigned any other value to best than the default. You have to place fellow = mylist.returnbest(); before the lines where you output best. But then use fellow for output instead of best, and you are free to make best a local variable within returnbest function...
mylist.printthem();
cout << best.return_id() << ' ' << best.return_skill() << ' ';
cout << best.return_years() << ' ' << best.return_value() << endl;
fellow = mylist.returnbest();
I don't know what you do in the printthem() member function, as you did not provide implementation of this function, but I assume you don't set variable best.So, you are printing an unset variable, which likely is initialized to zero.
What you want to do is more likely:
mylist.printthem();
best = mylist.returnbest();
cout << best.return_id() << ' ' << best.return_skill() << ' ';
cout << best.return_years() << ' ' << best.return_value() << endl;

C++ const pointers weird behaviour

Class C {
struct Something {
string s;
// Junk.
}
// map from some string to something.
map<string, Something> map;
// Some more code:
const Something *Lookup(string k) const {
const something *l = SomeLookUpFunction();
cout << l;
cout << &l->s;
cout << l->s;
return l;
}
}
// Some Test file
const C::Something *cs = C::Lookup("some_key");
cout << cs;
cout << &cs->s;
cout << cs->s;
The weird thing is this outputs:
* For lookup fucntion:
0x9999999
0x1277777
some_string
* For test code
0x9999999
0x1277777
000000000000000000000000000000000000000000 ....
In test file it gives a very long string of zeros, but the addresses are the same. Any idea what could be going wrong ?
Since you have not shared code for function SomeLookUpFunction, I have to guess that you are returning pointer to local object of type Something. This is a bad idea, see similar QA.
To start fixing your code, you should start by returning simple object, instead of pointer, as shown below:
// Some more code:
const Something lookup(string k) const {
const something l = SomeLookUpFunction(); // return simple object
cout << &l;
cout << &l.s;
cout << l.s;
return l; // same object
}
Of course you should improve the code by providing copy constructors for type something and even improving your map.

Am I creating my object arrays correctly? C++

I am trying to create a program that stores up to 50 players and the amount of wins they have. i.e. one use could be for for keeping track of sports teams and their amount of games won or something. However, I am having an issue when changing the player score. I am a beginner at C++, as you will probably be able to tell from my code. However, I have gone from knowing nothing whatsoever about coding to finishing three different tutorials and making a couple very simple command line programs in about a week. Any help as far as coding tips and how to make the scoreEdit() function work is thankfully accepted! Please find attached the players class, the scoreEdit() function, and my main function.
// for case 1. class that the scoreEdit() function uses!
class players
{
public:
void setName(string x)
{
name = x;
}
void addWin()
{
amtOfWins += 1;
}
void setWins(int x)
{
amtOfWins=x;
}
string getName()
{
return name;
}
int getWins()
{
return amtOfWins;
}
private:
string name;
int amtOfWins;
};
|
// for case 1. reads the file then stores each name in it's own player object and associates that with the amt of wins. Then rewrites all names and amtofwins to the file
void scoreEdit()
{
ifstream istats("stats.txt");
ofstream ostats("stats.txt");
if (istats.is_open() && ostats.is_open())
{
players player[50];
string tempName;
int tempWins;
while (istats >> tempName >> tempWins)
{
// reads in the name and amt of wins, and stores them in player object variables.
for (int x=0; x<50; x++)
{
player[x].setName(tempName);
player[x].setWins(tempWins);
}
}
string winner;
cout << "Who won?" << endl;
cin >> winner;
for (int x=0; x<50; x++)
{
if (player[x].getName()==winner)
{
player[x].addWin();
cout << "Ok. " << player[x].getName() << " has gained 1 point." << endl;
}
}
int x=0;
while (ostats << player[x].getName() << ' ' << player[x].getWins())
{
x++;
}
}
else
{
cout << "Quitting program. Stats could not be opened." << endl;
}
}
|
// main function
int main()
{
int x=0;
cout << "\n";
while (x==0) // not really sure if this is needed. Loops until case 4 or 5. Probably didn't need to use x. Oh well.
{
switch (choices())
{
case 0: // clears file
{
string decision;
cout << "ARE YOU SURE? This will wipe all data. (Type yes or no)\n" << endl;
cin >> decision;
if (decision=="yes")
{
clearStats();
cout << "STATS ARE WIPED.\n" << endl;
break;
}
else if (decision=="no")
{
cout << "Ok, stats will not be wiped.\n" << endl;
break;
}
else
{
cout << "Your input was not recognized. Stats will not be wiped.\n" << endl;
break;
}
}
case 1: // they want to add 1 to a player's score
{
scoreEdit();
break;
}
case 2: // they want to add a player
{
string name;
cout << "What is their name?" << endl;
cin >> name;
addPlayer(name);
break;
}
case 3: // they want to view the stats
{
readStats();
break;
}
case 4: // they want to quit. Simple! :)
{
return 0;
}
default: // means they did not input 1 2 3 or 4
{
cout << "Invalid input. Quitting program. Try again." << endl;
return 1;
}
}
}
}
EDIT: P.S. all my other functions/cases work. I just can't seem to find the problem with this one! Do I need to use a vector?
EDIT 2: Thanks for all the advice. Now, is there a way to check if the file still has data left to input? Right now, it inputs the lines just so that there are 50 player objects no matter what. Can I make a variable like int linesLeftInFile and use for (int x=0; x<linesLefInFile; x++)? Sorry for all the beginner questions.
You are trying to open the file twice, once for reading, once for writing, but in the same time.
...
ifstream istats("stats.txt");
ofstream ostats("stats.txt");
if (istats.is_open() && ostats.is_open())
...
When you open it for writing, like you do, the file content gets erased. The attempt to read the file will then fail, hence the big mess.
Open your stream first only to read, then close the stream, then open for write and put the results. Alternatively, you could consider an fstream with read and write.
By the way, it would be good practice for your players class to foresee a default constructor.
There are essentially two aspects:
A)
Am I creating my object arrays correctly? C++
which is related to persistence implementation and instance life-cycle issues, and
B)
EDIT: [...] Do I need to use a vector?
[...]
Right now, it inputs the lines just so that there are 50 player objects no matter what.
which is related to allocation and containers.
Both aspects of course intersect, e.g. a container like std::vector has much influence on the life-cycle of the instances it hosts.
Both aspects however have little to do with the functionality that is presented to the controller of the application, which is yet realized by the loop in main().
Thus, dealing with those aspects should be delegated to a class that separates them from the rest of the application.
Let's call that class-to-be PlayerDataBase.
PlayerDataBase will hold a container. But which?
Comlile-time fixed size arrays like
Player c_arra_players[50];
// or
std::array<Player,50> std_array_players;
are out of ansatz anyway; there is an external datum of arbitrary entity count which governs the multiplicity; thus the container must support run-time re-sizing.
A std::vector<Player> is plain and simple to use, but there's a seach to be performed
for (int x=0; x<50; x++) {
if (player[x].getName()==winner)
{
player[x].addWin();
cout << "Ok. " << player[x].getName() << " has gained 1 point." << endl;
}
}
which would have O(n) complexity on a sequential container like std::vector, whereas the same search on an associative like std::map (with the names used as keys) would be of O(log(n)) and about O(1) if std::unordered_map` would be used along with a perfect hash function.
On the other hand, adding and especially renaming players (use case: fixing typo in a nick) would be become more expensive for associative than sequential containers.
Especially for C++ learners it may be interesting to play a little with different internal storage representations, thus PlayerDataBase could be a template with a container template parameter.
There should only be one PlayerDataBase instance in the application and the controller should have a single point of access to that instance; thus it is meant to be a singleton.
The ifstream/ofstream/fstream issues can be dealt with quiet simple by PlayerDataBase - it's (private) ctor reads, or creates and reads, a stats file from an ifstream which is closed after the ctor completed.
Persisting the data is done by a flush() function, which opens an ofstream by using the _file_name member, writes, and that stream is closed on flush terminating.
///
///#brief modified singleton pattern
///
template<template<typename...> class Cont>
class PlayerDataBase : private players_container_adaptor<Cont> {
private:
using Traits = players_container_traits<Cont>;
public: // type interface
using container_type = typename Traits::players_container_type;
using Base = players_container_adaptor<Cont>;
struct no_such_player : public std::runtime_error {
no_such_player(const std::string& msg) : std::runtime_error(msg) {}
};
public: // creation interface
static PlayerDataBase& instance(const std::string& file_name = ::FILE_NAME)
throw (std::runtime_error) {
// automatically dtored
static PlayerDataBase _instance_(file_name); // throws
return _instance_;
}
public: // behaviour interface
void flush () const throw(std::ios::failure);
void addWin(const std::string& key) throw (no_such_player);
private: // types and function name resolution
using Adaptor = Base;
using Adaptor::getPlayer; // throws std::runtime_error
// container specific lookup,
using Adaptor::make_inserter; // std::copy(..,..,,make_inserter(Cont & c));
using Adaptor::emplace; // dispatches to container_specific emplace
using _asset_type = typename Traits::asset_type;
constexpr static auto BAD_AND_FAIL = std::ios::badbit | std::ios::failbit;
constexpr static auto BAD_ONLY = std::ios::badbit;
struct no_stats_file : public std::runtime_error {
no_stats_file(const std::string& msg) : std::runtime_error(msg) {}
};
private: // lifecycle interface
PlayerDataBase(const std::string&) throw (std::runtime_error);
~PlayerDataBase() noexcept;
// This is a singleton
PlayerDataBase(const PlayerDataBase&) = delete;
PlayerDataBase(PlayerDataBase&&) = delete;
PlayerDataBase& operator=(PlayerDataBase&&) = delete;
PlayerDataBase& operator=(const PlayerDataBase&) = delete;
PlayerDataBase& operator=(PlayerDataBase&) = delete;
private: // helpers
void create_data_file() const throw(std::ios::failure);
private: // state
container_type _players;
std::string _file_name;
}; // class PlayerDataBase
#include "playerdatabase.inl"
The required traits and adaptor templates could look like this:
template<template<typename...> class C> struct players_container_traits {};
// [...]
template<> struct players_container_traits<std::map> {
using players_container_type = std::map<std::string, Player>;
using player_ckv_type = players_container_type::value_type;
using player_kv_type = std::pair<std::string, Player>;
using asset_type = player_kv_type;
}; // struct player_container_traits<std::map>
// [...]
template<template<typename...> class C> struct players_container_adaptor{};
// [...]
template<> struct players_container_adaptor<std::map> {
using Traits = players_container_traits<std::map>;
using players_map_type = Traits::players_container_type;
static void post_ctor(players_map_type&) {
/* nop */
}
static Player& getPlayer(players_map_type& m, const std::string& key)
throw (std::runtime_error) {
auto it = m.find(key);
if (it == m.end())
throw std::runtime_error(key + " unknown");
return it->second;
} // addWin(players_map_t&,...)
static auto make_inserter(players_map_type& m)
-> decltype(std::inserter(m, m.begin())) {
return std::inserter(m, m.begin());
}
template<typename... Targs, typename K, typename... Args>
static void emplace(std::map<Targs...>& m, K&& k, Args&&... args) {
K _k = k;
m.emplace(std::piecewise_construct,
std::forward_as_tuple(_k),
std::forward_as_tuple(k, args...));
}
}; // template<> struct players_container_adaptor<std::map>
I postulated that the business class would be renamed to Player and gets a bunch of (noexcept) ctors and some related custom operators for << >>' forostreamresp.istream` output.
Happy hacking!
Addendum Jul 20th 2014
I don't see much reason for random sequence access anyway, thus if a sequential container would nevertheless be used (what I would not advocate due to the find complexity) then std::list would be more appropriate; that saves costs for possible complete, and expensive, re-allocations std::vector suffers from.

Cannot get the value from a class in C++?

I just came from Java and Python world to C++ world, and faced a problem while trying to get the value from a public const function of a class.
I have a class as follows:
class CMDPoint
{
public:
CMDPoint();
CMDPoint(int nDimensions);
virtual ~CMDPoint();
private:
int m_nDimensions; // the number of dimensions of a point
float* m_coordinate; // the coordinate of a point
public:
const int GetNDimensions() const { return m_nDimensions; }
const float GetCoordinate(int nth) const { return m_coordinate[nth]; }
void SetCoordinate(int nth, float value) { m_coordinate[nth] = value; }
};
Ultimately, I wish to write all the clusterPoints in clusterPointArray into the file. However, now I am just testing it with the first clusterPoint (thus, GetCoordinate(0)).
ofstream outFile;
outFile.open("C:\\data\\test.txt", std::ofstream::out | std::ofstream::app);
for (std::vector<CMDPoint> ::iterator it = clusterEntry->clusterPointArray.begin(); it != clusterEntry->clusterPointArray.end(); ++it)
{
outFile << ("%f", (*it).GetCoordinate(0)); // fails
outFile << " ";
}
outFile << "\n";
outFile.close();
The problem is I only see the " " in the file. No coordinate has been written in. Did I do anything wrong while fetching the value from const float GetCoordinate(int nth)?
try to change this
outFile << ("%f", (*it).GetCoordinate(0)); // fails
to this:
outFile << (*it).GetCoordinate(0); // OK
Because the ("%f", (*it).GetCoordinate(0)) represents nothing , only a enumerations of expressions separated by , . It will not be evaluated into a pair of objects as in java is i think.
Edit:("%f", (*it).GetCoordinate(0)) actually evaluates to the last element that is (*it).GetCoordinate(0) ( PlasmaHH comment ) so it should still print something. However if nothing is printed then the collection clusterEntry->clusterPointArray could be empty and the code inside the for loop might not be executed ever.
Hope this helps,
Razvan.
outFile << it->GetCoordinate(0);