C++, How to read data from file and insert in container: Map - c++

I have a problem with fill container map with information . I read the information with operator>> but it gives me these errors in my second class CDealer. no operator ">>" matches these operands and binary">>": no operator found which takes a right-hand operand of type 'const CStock'(or these is no acceptable conversion)
I have three classes: CStock, CDealer who have map<CStock, pair<unsigned, double>> Stock; and CShop who have vector<CDealer*> Dealers;
I'll be very thankful if someone can help me.
This is my operators << and >> in CStock
ostream &operator << (ostream &toStream, const CStock &S){
toStream << "Name Stock: " << S.m_strName_stock << " Producer: " << S.m_strProducer <<
"Date: " << S.Date;
return toStream;
}
istream &operator >> (istream &fromStream, CStock &S)
{return fromStream >> S.m_strName_stock >> S.m_strProducer >> S.Date;}
This is my operators << and >> in CDealer
`
ostream &operator << (ostream &toStream, CDealer &D)
{
toStream << "Name Dealer: " << D.m_strName_Dealer << " Agent: " << D.m_strAgent <<
"Address: " << D.m_strAddress;
map<CStock, pair<unsigned, double>>::const_iterator it = D.Stock.begin();
while (it != D.Stock.end())
{
toStream << it->first <<it->second.first << it->second.second;
}
return toStream;
}
istream &operator >> (istream &fromStream, CDealer &D)
{
map<CStock, pair<unsigned, double>>::iterator it = D.Stock.begin();
fromStream >> D.m_strName_Dealer >> D.m_strAgent >> D.m_strAddress;
while (it!= D.Stock.end())
{fromStream >> it->first >> it->second.first >> it->second.second;}
return fromStream
}
And this is the constructor with parameter: file name and these tho operators <<, >>
CShop::CShop(const string &fname)
{
CDealer c;
fstream File(fname, ios::in);
if (File.is_open())
{
File >> m_strNameShop;
File >> m_strCity;
while (File>>c)
{
Dealers.push_back(new CDealer(c));
}
File.close();
}
else
throw "ERROR! ";
}
ostream &operator << (ostream &toStream, const CShop &S)
{
toStream << "Name Shop: " << S.m_strNameShop << " City: " << S.m_strCity;
vector<CDealer* >::const_iterator it = S.Dealers.begin();
while (it != S.Dealers.end())
{
CDealer* dealerPtr = *it++;
toStream << *dealerPtr<< endl;
}
return toStream;
}
istream &operator >> (istream &fromStream, CShop &D)
{
return fromStream >> D.m_strNameShop >> D.m_strCity;
}
And in the end main()
#include"CDealer.h"
#include"CShop.h"
#include<iostream>
#include<string>
#include <stdlib.h>
#include<vector>
using namespace std;
int main()
{
CShop SS1("data.txt");
cout << SS1;
system("pause");
return 0;
}

The implmentation of istream &operator >> (istream &fromStream, CDealer &D)
is poorly thought through.
The objective of the function is to read data from an input stream and flesh out the contents of the CDealer object. Normally the operator<< and operator>> functions work in tandem so that what you write using operator<< can be read back using operator>>.
From that point of view, even the operator<< function needs to revamped.
Here's one implementation that should work as long as there are no spaces in the string objects. If you have space in your string objects, the code needs be changed.
std::ostream& operator<<(std::ostream &toStream, CDealer cosnt& D)
{
// Write the name of the dealer, without any decorative text.
// Add a space character to the output so you can read the name back.
toStream << D.m_strName_Dealer << " ";
// Do the same for name of the agent and address.
toStream << D.m_strAgent << " ";
toStream << D.m_strAddress << " ";
// Write the number of items in Stock first.
// This will be necessary when reading the data.
toStream << D.Stock.size() << " ";
// Now write the Stock items, which spaces between each field you write.
map<CStock, pair<unsigned, double>>::const_iterator it = D.Stock.begin();
while (it != D.Stock.end())
{
toStream << it->first << " " << it->second.first << " " << it->second.second << " ";
}
return toStream;
}
std::istream &operator>>(std::istream &fromStream, CDealer &D)
{
// Read the name of the dealer.
fromStream >> D.m_strName_Dealer;
// Read the name of the agent.
fromStream >> D.m_strAgent;
// Read the address.
fromStream >> D.m_strAddress;
// Read the number of items.
size_t num;
fromStream >> num;
// Now read the Stock items.
for ( size_t i = 0; i < num; ++i )
{
// Obtained the types for key, val1, and val2 from
// the error message posted in a comment by OP.
CStock key;
int val1;
double val2;
fromStream >> key >> val1 >> valu2;
// Add the item to Stock.
D.Stock.insert(std::make_pair(key, std::make_pair(val1, val2)));
}
return fromStream
}

Problem
The problem can be reduced to the following case:
#include <map>
int main(void) {
std::map<int, int> val;
auto it = val.begin();
it->first = 10;
}
The key values in a std::map are const (the pair of key and value is defined as std::pair<const Key, T>), so for
map<CStock, pair<unsigned, double>>
we have a map containing pairs of
std::pair<const CStock, pair<unsigned, double>>
which makes it->first in
fromStream >> it->first >> it->second.first >> it->second.second;
a const CStock and results in the reported error.
This all makes sense because the map is ordered by the key, so if you could change the key whenever you wanted to whatever you wanted the map would soon be incoherent.
Documentation on std::map
Solution
There is no simple solution. You cannot do this. You must either make the items and then place them in the map or use one of the emplace family of functions to construct them directly in the map. Once they are in the map you can change the value, but not the key. You could remove the item, and thus the key, and reinsert with a different key, but why are you doing this in the first place? I recommend a rethink of your logic.

Related

How to improve operator overloading for my class?

I have started learning C++. My teacher gave an assignment. I completed it (some polishing work is left) and everything seems to work but there is redundancy. My major issue is overloading.
How to improve overloading in my class. Rather than writing all four functions (two for fstream and two for iostream), is it possible to write only two ? Any other suggestions to improve the code further ?
#include <iostream>
#include <fstream>
#include <string>
#include "../../my_func.hpp"
#define usi unsigned short int
using namespace std;
class book
{
public:
usi book_id = 0, price = 0, no_of_pages = 0, year_of_publishing = 0;
string author_name = "NONE", publisher = "NONE";
book(usi b_id = 0, usi b_price = 0, usi b_no_of_pages = 0, usi b_year_of_publishing = 0,
const string& b_author_name = "NONE", const string& b_publisher = "NONE")
{
book_id = b_id;
price = b_price;
no_of_pages = b_no_of_pages;
year_of_publishing = b_year_of_publishing;
author_name = b_author_name;
publisher = b_publisher;
}
friend fstream& operator >> (fstream& is, book& obj);
friend fstream& operator << (fstream& os, const book& obj);
friend istream& operator >> (istream &is, book& obj);
friend ostream& operator << (ostream &os, const book& obj);
};
fstream& operator >> (fstream &is, book& obj)
{
char ch;
is >> obj.book_id >> obj.price
>> obj.no_of_pages >> obj.year_of_publishing;
is.ignore(1, '\n'); //To take care of new line character
getline(is, obj.author_name);
getline(is, obj.publisher);
return is;
}
fstream& operator << (fstream &os, const book& obj)
{
os.operator<<(obj.book_id) << '\n' //calling operator function cuz it works
<< obj.price << '\n'
<< obj.no_of_pages << '\n'
<< obj.year_of_publishing << '\n';
os << obj.author_name << '\n'
<< obj.publisher << '\n';
return os;
}
istream& operator >> (istream &is, book& obj)
{
is >> obj.book_id >> obj.price
>> obj.no_of_pages >> obj.year_of_publishing;
is.ignore(1, '\n'); //To take care of new line character
getline(is, obj.author_name);
getline(is, obj.publisher);
return is;
}
ostream& operator << (ostream &os, const book& obj)
{
os << obj.book_id << '\n'
<< obj.price << '\n'
<< obj.no_of_pages << '\n'
<< obj.year_of_publishing << '\n'
<< obj.author_name << '\n'
<< obj.publisher << '\n';
return os;
}
int main()
{
string path = ".\\C++_Experiment\\Exp-7\\Files\\Source.txt";
book b1(12, 3000, 100, 2003, "Lol", "Pew"), b2, b3;
fstream fio;
fio.open(path, ios::out | ios::app | ios::in);
if(fio) fio << b1;
else cout << "error";
fio.seekg(0, ios::beg);
if(fio) fio >> b2 >> b3;
cout << b2 << b3;
fio.close();
cout << "DONE";
return 0;
}
You only need two overloads here. ifstream and ofstream inherit from istream and ostream respectively so if you have
friend istream& operator >> (istream &is, book& obj);
friend ostream& operator << (ostream &os, const book& obj);
then those will work with cout and cin, and any fstream or stringstream objects as they also inherit from istream and ostream.
Some other suggestions apart from overloading only for std::istream and std::ostream:
Don't using namespace std;. Prefer to prefix your STL types and algorithms with std::.
Prefer using to typedef, and, in this case, to #define. You could limit the usi alias to the book scope.
Turn book into a struct: if everything within the class book is public. This way, you don't need the operator overloads to be friends of book, since they can directly access the members within a book instance.
Prefer to use member initialization lists, i.e. initialize your member variables right after the list of parameters. You can also provide a default member initialization value at the declaration point; but, in this case, a single {} will zero initialize the usi types. I wouldn't use default parameter values for the constructor in this example.
Consider that, if you provide a custom constructor, the default constructor, copy constructor, move constructor, copy assignment operator, and move assignment operator will be deleted, and thus not available.
[Demo]
#include <iostream>
#include <string>
struct book {
using usi = unsigned short int;
usi book_id{};
usi price{};
usi no_of_pages{};
usi year_of_publishing{};
std::string author_name{"NONE"};
std::string publisher{"NONE"};
book() = default;
book(const book& other) = default;
book& operator=(const book& other) = default;
book(book&& other) noexcept = default;
book& operator=(book& other) noexcept = default;
~book() = default;
book(usi b_id,
usi b_price,
usi b_no_of_pages,
usi b_year_of_publishing,
const std::string& b_author_name,
const std::string& b_publisher)
: book_id{b_id}
, price{b_price}
, no_of_pages{b_no_of_pages}
, year_of_publishing{b_year_of_publishing}
, author_name{b_author_name}
, publisher{b_publisher}
{}
};
std::istream& operator>>(std::istream& is, book& obj) {
is >> obj.book_id >> obj.price >> obj.no_of_pages >> obj.year_of_publishing;
is.ignore(1, '\n'); // To take care of new line character
getline(is, obj.author_name);
getline(is, obj.publisher);
return is;
}
std::ostream& operator<<(std::ostream& os, const book& obj) {
return os
<< "["
<< obj.book_id << ", "
<< obj.price << ", "
<< obj.no_of_pages << ", "
<< obj.year_of_publishing << ", "
<< obj.author_name << ", "
<< obj.publisher << "]";
}
int main() {
book b1{ 12, 3000, 100, 2003, "Lol", "Pew" };
std::cout << "b1: " << b1 << "\n";
book b2{};
std::cin >> b2;
std::cout << "b2: " << b2;
}
Make book an aggregate. You can get rid of a lot of code if you forget about initializing the std::string members to "NONE" (you can always check for empty string at operator<< and output "NONE if needed). You get rid of the custom constructor and, with it, of all the other default constructors and assignment operators.
[Demo]
#include <iostream>
#include <string>
struct book {
using usi = unsigned short int;
usi book_id{};
usi price{};
usi no_of_pages{};
usi year_of_publishing{};
std::string author_name{};
std::string publisher{};
};
std::istream& operator>>(std::istream& is, book& obj) {
is >> obj.book_id >> obj.price >> obj.no_of_pages >> obj.year_of_publishing;
is.ignore(1, '\n'); // To take care of new line character
getline(is, obj.author_name);
getline(is, obj.publisher);
return is;
}
std::ostream& operator<<(std::ostream& os, const book& obj) {
return os
<< "["
<< obj.book_id << ", "
<< obj.price << ", "
<< obj.no_of_pages << ", "
<< obj.year_of_publishing << ", "
<< (obj.author_name.empty() ? "NONE" : obj.author_name) << ", "
<< (obj.publisher.empty() ? "NONE" : obj.publisher) << "]";
}
int main() {
book b1{ 12, 3000, 100, 2003, "Lol", "" };
std::cout << "b1: " << b1 << "\n";
book b2{};
std::cin >> b2;
std::cout << "b2: " << b2;
}

Error when overloading opertor>> for string memebrs in the class

When I input the value for the string types, for examples:
_ho I typed Peter
_hoten I typed Peter Parker
_ten I typed Marry
My output on the screen was:
Peter Peter Marry
Here is my code:
class SinhVien
{
private:
string _ho;
string _tenlot;
string _ten;
public:
static int InstanceCount;
SinhVien();
string ToString() const;
friend istream& operator>>(istream& in, SinhVien* p);
friend ostream& operator<<(ostream& out, const SinhVien* p);
~SinhVien();
};
istream& operator>>(istream& in, SinhVien *p)
{
cout << "Nhap ho: \n";
in >> p->_ho;
rewind(stdin);
cout << "Nhap ten lot: \n";
in >> p->_tenlot;
rewind(stdin);
cout << "Nhap ten: \n";
in >> p->_ten;
return in;
}
string SinhVien::ToString() const
{
stringstream writer;
writer << _ho << " " << _tenlot << " " << _ten << "\n";
return writer.str();
}
ostream& operator<<(ostream &out, const SinhVien* p)
{
out << p->ToString();
return out;
}
void main()
{
SinhVien *a;
a = new SinhVien();
cin >> a;
cout << a;
cout << "\nTo string:\n";
cout << a->ToString();
delete a;
system("pause");
}
In your std::basic_istream::operator>> overload, you need to use std::geline() instead of std::cin >>, so that you can get complete input name with spaces.
Secondly, you should pass the reference of the object pointer, so that the change will be applied to the passed object, not to its copy.
Fix:
std::istream& operator>>(std::istream& in, SinhVien* &p)
{ // ^^ take ref of object you pass, so that the change will be applied to the object, not to the copy of it.
std::cout << "Nhap ho: \n";
std::getline(in, p->_ho); // change
std::rewind(stdin);
std::cout << "Nhap ten lot: \n";
std::getline(in, p->_tenlot); // change
std::rewind(stdin);
std::cout << "Nhap ten: \n";
std::getline(in, p->_ten); // change
return in;
}
Above will work for one single input. However, the case of multiple inputs and use of std::cin >> in the main() can cause again some input skipping problems. Thanks to #Yksisarvinen pointing this out.
You can read more in this SO post: Why does std::getline() skip input after a formatted extraction?
Side note: In modern C++, you do not need to manage the raw pointers, anymore. Because you have smart pointers which will manage your object's lifetime as it goes out of scope. So use it whenever its possible.
That means you can do something like this: SEE LIVE
#include <memory>
class SinhVien
{
private: // memebrs
public:
// other member functions
friend istream& operator>>(istream& in, const std::unique_ptr<SinhVien> &p);
friend ostream& operator<<(ostream& out, const std::unique_ptr<SinhVien> &p);
};
std::istream& operator>>(std::istream& in, const std::unique_ptr<SinhVien> &p)
{
std::cout << "Nhap ho: \n";
std::getline(in, p->_ho); // change
std::cout << "Nhap ten lot: \n";
std::getline(in, p->_tenlot); // change
std::rewind(stdin);
std::cout << "Nhap ten: \n";
std::getline(in, p->_ten); // change
return in;
}
std::ostream& operator<<(std::ostream &out, const std::unique_ptr<SinhVien> &p)
{
return out << p->ToString();
}
int main()
{
auto a = std::make_unique<SinhVien>();
std::cin >> a;
std::cout << a;
std::cout << "\nTo string:\n";
std::cout << a->ToString();
return 0;
}

C++, container vector of pointers, overloading operator<<

I am trying to take out to the console window my vector of pointers but still doesn't work. I can fill the vector but when I try to cout<< my object noting comes out on the console window.
This is header file with declarations of the private class members and functions:
#pragma once
#include"CDealer.h"
#include<vector>
#include<fstream>
#include<iterator>
class CShop
{
public:
CShop();
~CShop();
CShop(const string &fname);
friend ostream &operator << (ostream &toStream, const CShop &S);
friend istream &operator >> (istream &fromStream, CShop &S);
private:
string m_strNameShop;
string m_strCity;
vector<CDealer*> Dealers;
};
This is my constructor with file from where I bring the data
#include "CShop.h"
CShop::CShop(){}
CShop::~CShop(){}
CShop::CShop(const string &fname)
{
fstream File(fname, ios::in);
if (File.is_open())
{
CDealer c;
File >> m_strNameShop;
File >> m_strCity;
while (File.is_open())
{
File >> c;
Dealers.push_back(new CDealer(c));
}
File.close();
}
else
throw "ERROR! ";
}
and this is overloading operator<<:
ostream &operator << (ostream &toStream, const CShop &S)
{
return toStream << "Name Shop: " << S.m_strNameShop << " City: " << S.m_strCity;
vector<CDealer *>::const_iterator it = S.Dealers.begin();
while (it != S.Dealers.end())
{
toStream << "Dealer " << *it++;
}
}
So in the end my main
#include"CShop.h"
#include<iostream>
#include<string>
#include <stdlib.h>
#include<vector>
using namespace std;
int main()
{
CShop SS1("data.txt");
cout << SS1;
system("pause");
return 0;
Please help Me :)
You have a premature return in the function.
return toStream << "Name Shop: " << S.m_strNameShop << " City: " << S.m_strCity;
//^^^
Remove it.
toStream << "Name Shop: " << S.m_strNameShop << " City: " << S.m_strCity;
Add a return before the end of the function.
std::ostream& operator<<(std::ostream& toStream, const CShop &S)
{
toStream << "Name Shop: " << S.m_strNameShop << " City: " << S.m_strCity;
vector<CDealer *>::const_iterator it = S.Dealers.begin();
while (it != S.Dealers.end())
{
toStream << "Dealer " << *it++;
}
return toStream;
}
Suggestions for further improvement.
The line
toStream << "Dealer " << *it++;
will print only the pointer values. If you want to print the details of the object the pointers point to, change it to:
Dealer* dealerPtr = *it++;
toStream << "Dealer " << *dealerPtr;
The following block is not right. File.is_open() will be always true in this block.
while (File.is_open())
{
File >> c;
Dealers.push_back(new CDealer(c));
}
Change it to:
while (File >> c)
{
Dealers.push_back(new CDealer(c));
}

gmtime won't work for a object read with overloaded operator

So I've made a class for a location,in which I store it's coordonates and it's time,in utc. I overloaded the >> operator like this
friend ifstream& operator >>(ifstream& in, loc_list& l)
{
char bf[40];
in >> bf;
l.setID(bf);
long t=0;
in >> l.utc;
//l.setTime(t);
double point;
in >> point;
l.p.setX(point);
in >> point;
l.p.setY(point);
in >> l.speed;
return in;
}
and the << operator like this:
friend ostream& operator <<(ostream& out,const loc_list &l)
{
out << l.id << endl;
out << put_time(gmtime(&l.utc),"%c %Z" )<< endl;
//out << l.utc << endl;
out << l.p.getX() << endl;
out << l.p.getY() << endl;
out << l.speed;
return out;
}
however,the gmtime in the << operator works only when I create an object using the constructor. When I read it with cin>> it breaks.
I've debugged the program but the objects contains the right data
print from debugger
so,any thoughts?
The counterpart of put_time is get_time.
I suggest using:
std::tm tm
in >> get_time(&tm, "%c %Z");
and then update l.utc from tm.
l.utc = mktime(&tm);

reading and displaying strings from file

I stored some strings in a text file using overloaded insertion operator.
ostream & operator << (ostream & obj,Person & p)
{
stringstream ss;
ss << strlen(p.last) << p.last << strlen(p.first) << p.first
<< strlen(p.city) << p.city << strlen(p.state) << p.state;
obj << ss.str();return obj;
}
The contents of file look like this
4bill5gates7seattle10washington
I now need to read the length first and display the string.And continue to display all the strings.How to do this with an overloaded extraction operator?
Read it one character at a time and use std::string::push_back to append to a string variable. There's a std::stoi which will convert your string lengths to an integer. Might I suggest that when you create your text file that you put a whitespace after your integer length, then you can just cin >> string_length and avoid using if statements to control when you've found the end of a number, or the beginning of a new string.
Also, it would be more beneficial, if you showed us what you've attempted, so that we could help you more specifically.
You may do:
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>
int main() {
std::istringstream in("4bill5gates7seattle10washington");
std::vector<std::string> strings;
unsigned length;
while(in >> length) {
std::string s;
if(in >> std::setw(length) >> s)
strings.push_back(s);
}
for(const auto& s : strings)
std::cout << s << '\n';
}
Disclaimer: The file format is evil.
Note: This does not extract a 'Person', but fields. I leave that to you.
Make operator << like this
ostream & operator >> ( ostream & obj, Person & p )
{
obj << strlen( p.last ) << " " << p.last << " " << strlen( p.first ) << " " << p.first << " "
<< strlen( p.city ) << " " << p.city << " " << strlen( p.state ) << " " << p.state;
return obj;
}
and operator >> like this
istream & operator >> ( istream & obj, Person & p )
{
obj >> p.last >> p.first >> p.city >> p.state;
return obj;
}