Using streambuf when sending/getting struct - c++

I'm working on boost::asio::streambuf and found out that I can send/get a struct using it, but when I send a struct I just can't get it as I have sent it. The documentation says that one should use commit() and consume(), but where should I use them here?
struct person
{
int m_id;
std::string m_message;
};
std::istream& operator>>(std::istream& in, struct person& p)
{
return in >> p.m_id >> p.m_message;
}
std::ostream& operator<<(std::ostream& out, struct person& p)
{
return out << p.m_id << " " << p.m_message;
}
int main()
{
boost::asio::streambuf buf;
std::ostream out(&buf);
person p;
p.m_id = 1;
p.m_message = "Hello World!";
out << p;
std::istream in(&buf);
person p1;
in >> p1;
cout << "ID: " << p1.m_id << endl;
cout << "Message: " << p1.m_message << endl;
return 0;
}
The problem is with strings so when I type only "hello" (without world), it works fine, but if I add "world!" as shown above it just doesn't see the added "world!", why?

There are a number of issues.
Firstly, make the arguments const& when possible:
std::ostream &operator<<(std::ostream &out, person const &p) {
Secondly, make sure the streams flush to the buffer. I think it's good practice to limit the lifetime of the ostream or istream instances
Thirdly, choose a format that will be robust. Your sample already had bigger problems, when you had m_id = 1 and m_message = "123" (can you see it?).
In text formats you need either fixed-length fields or a delimiting protocol. Let's fix it:
std::ostream &operator<<(std::ostream &out, person const &p) {
return out << p.m_id << ";" << p.m_message.length() << ";" << p.m_message;
}
Now when reading it back you will see how much more precise you need to be:
std::istream &operator>>(std::istream &in, person &p) {
char separator;
size_t length;
bool ok = in >> p.m_id
&& in >> separator && separator == ';'
&& in >> length
&& in >> separator && separator == ';'
;
if (ok) {
p.m_message.resize(length);
in.read(&p.m_message[0], length);
p.m_message.resize(in.gcount());
}
// ensure the expected number of bytes were read
ok = ok && (p.m_message.length() == length);
if (!ok)
in.setstate(std::ios::failbit);
return in;
}
Yikes. Really? Yes really. At a minimum!
Do error handling
Full Demo
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
struct person {
int m_id;
std::string m_message;
};
std::ostream &operator<<(std::ostream &out, person const &p) {
return out << p.m_id << ";" << p.m_message.length() << ";" << p.m_message;
}
std::istream &operator>>(std::istream &in, person &p) {
char separator;
size_t length;
bool ok = in >> p.m_id
&& in >> separator && separator == ';'
&& in >> length
&& in >> separator && separator == ';'
;
if (ok) {
p.m_message.resize(length);
in.read(&p.m_message[0], length);
p.m_message.resize(in.gcount());
}
// ensure the expected number of bytes were read
ok = ok && (p.m_message.length() == length);
if (!ok)
in.setstate(std::ios::failbit);
return in;
}
int main() {
boost::asio::streambuf buf;
std::ostream(&buf) << person{ 1, "Hello World!" };
person received;
if (std::istream(&buf) >> received) {
std::cout << "ID: " << received.m_id << std::endl;
std::cout << "Message: " << received.m_message << std::endl;
} else {
std::cout << "Couldn't receive person\n";
}
}
Prints
ID: 1
Message: Hello World!
BONUS
C++14 added std::quoted:
#include <iomanip>
std::ostream &operator<<(std::ostream &out, person const &p) { return out << p.m_id << std::quoted(p.m_message); }
std::istream &operator>>(std::istream &in, person &p) { return in >> p.m_id >> std::quoted(p.m_message); }
Which, in this case, also does the job: Live On Coliru

From http://en.cppreference.com/w/cpp/string/basic_string/operator_ltltgtgt (emphasis and ellipsis mine):
2) ... reads characters from is and appends them to str ... until one
of the following conditions becomes true:
...
...
std::isspace(c,is.getloc()) is true for the next character c in is (this whitespace character remains in the input stream). ...
Basicaly what this means is that if you extract a string from an istream using operator >> it stops at white spaces.
If you want to get everything from the stream into your string there are plenty of questions asking that (Like this one or this one).

Related

Array based on strings, char list conversions

I create a multiple table of string type. I keep variables inside (int, string). It gives me an error:
[Error] cannot convert 'std::string {aka std::basic_string}' to 'char' in assignment
I've created a tree-shaped suite of functions.The program create a multiple array from a file with this format:
11 10 2001
CSKA Moscow
12 1
Bayern Munich
...
Program:
void llegir(std::fstream &_contingut, std::string *_taula) {
//declaro variables
int dia, mes, any, puntsLocal, puntsVisitant, i = 0;
std::string equipLocal, equipVisitant;
while (!(_contingut.eof())) {
//llegeixo arxiu
_contingut >> dia >> mes >> any; //primera linea
_contingut.ignore();
getline(_contingut, equipLocal); //segona linea
_contingut >> puntsLocal >> puntsVisitant; //tercera linea
_contingut.ignore();
getline(_contingut, equipVisitant); //quarta linea
_taula[i][0] = dia;
_taula[i][1] = mes;
_taula[i][2] = any;
_taula[i][3] = equipLocal.c_str();
_taula[i][4] = puntsLocal;
_taula[i][5] = equipVisitant.c_str();
_taula[i][6] = puntsVisitant;
i++;
}
}
void creartaulaDelFitxer(std::string _fitxer, std::string *_taula, int &n_taula) {
std::fstream arxiu;
arxiu.open(_fitxer, std::fstream:: in );
if (arxiu.is_open()) {
std::cout << "existeix";
} else {
std::cout << "ERROR OBERTURA FITXER";
}
llegir(arxiu, _taula);
}
int main(int argc, char** argv) {
std::string fitxer;
std::string eurolliga[300][7];
int n_taula = 0;
std::cout << "INTRODUEIX NOM FITXER:" << std::endl;
std::cin >> fitxer;
creartaulaDelFitxer(fitxer, *eurolliga, int n_taula);
}
You are mixing pointers, chars and strings which will certainly cause a lot of headache. Try to use the standard containers, like std::string and std::vector. If you need many strings, put them in a vector. When you have a collection of data like
11 10 2001
CSKA Moscow
12 1
Bayern Munich
that describes some entity, create a class for it. You can then add streaming operators for that class to read in one of these entities. If you have a collection of entities, make a container and add streaming operators for that too.
Example:
#include <iostream>
#include <fstream>
#include <vector>
class Game {
std::string equipLocal{};
std::string equipVisitant{};
int dia{}, mes{}, any{};
int puntsLocal{}, puntsVisitant{};
public:
friend std::istream& operator>>(std::istream&, Game&);
friend std::ostream& operator<<(std::ostream&, const Game&);
};
// read one entity from an istream
std::istream& operator>>(std::istream& is, Game& g) {
if(is >> g.dia >> g.mes >> g.any) {
is.ignore();
if(std::getline(is, g.equipLocal) && (is >> g.puntsLocal >> g.puntsVisitant)) {
is.ignore();
std::getline(is, g.equipVisitant);
}
}
return is;
}
// write one entity to an ostream
std::ostream& operator<<(std::ostream& os, const Game& g) {
return os << g.dia << " " << g.mes << " " << g.any << "\n"
<< g.equipLocal << "\n"
<< g.puntsLocal << " " << g.puntsVisitant << "\n"
<< g.equipVisitant << "\n";
}
class EuroLiga {
std::vector<Game> games{};
public:
bool Load(const std::string& filename) {
std::ifstream arxiu(filename);
if(arxiu) {
games.clear();
arxiu >> *this; // use this class' friend, operator>>
return true;
} else
return false;
}
// support for basic non-const iteration over the 'games'
std::vector<Game>::iterator begin() { return games.begin(); }
std::vector<Game>::iterator end() { return games.end(); }
friend std::istream& operator>>(std::istream&, EuroLiga&);
};
// read all entities from an istream
std::istream& operator>>(std::istream& is, EuroLiga& el) {
Game tmp;
while(is >> tmp) {
el.games.push_back(std::move(tmp));
}
return is;
}
int main() {
EuroLiga euroliga;
std::string fitxer;
std::cout << "INTRODUEIX NOM FITXER: ";
std::cin >> fitxer;
euroliga.Load(fitxer);
// display all entities read from the file
for(auto& g : euroliga) {
std::cout << g << "\n";
}
}
void llegir(std::fstream &_contingut, std::string *_taula)
Gets a pointer to a string called _taula, this is probably your array.
However you assign something to your array like this:
_taula[i][0] = dia; // allowed, but bad because char is basically a number.
[...]
_taula[i][3] = equipLocal.c_str(); // not allowed, you are assigning a char pointer to a char.
taula[i] is the i-th string in your array. And by putting [0] you assign to the first character in that string. dia is an integer though.
For example
std::string[] = {"Hello", "world", "I", "am", "alive"};
std::cout << string[1] << std::endl; // output "world"
std::cout << string[1][0] << std::endl; // output 'w'
You can not assign a string to a single character.
As a side note, you should look into declaring an enumeration for your array index (and a constant for it's size) to make it more clear and improve maintainability.
What you should probably be doing is create a struct or class for your, whatever it is
struct whateverItIs {
int dia, mes, any, puntsLocal, puntsVisitant;
std::string equipLocal, equipVisitant;
};
Make a new instance of that in your llegir and push it to the back of a std::vector you get by reference.
Just remember to delete() them later especially before that vector goes out of scope.

C++ File handling error(unhandled exception)

I am trying to write a program where you register a bank account and modify it. This is the code (Not full but just trying to experiment with file handling; I am a beginner at programming)
#include <iostream>
#include <fstream>
#include <string>
std::ofstream outfile;
std::ifstream infile;
std::fstream inout;
struct bank{
int acc_nr;
std::string emri;
std::string mbiemri;
};
std::istream& operator>> (std::ifstream& in, bank& klient)
{
in >> klient.emri;
in >> klient.mbiemri;
in >> klient.acc_nr;
return in;
}
std::ostream& operator<< (std::ofstream& out, bank& klient)
{
out << klient.emri << std::endl;;
out << klient.mbiemri << std::endl;
out << klient.acc_nr << std::endl;
return out;
}
std::ostream& operator<< (std::fstream& out, bank& klient)
{
out << klient.emri << std::endl;;
out << klient.mbiemri << std::endl;
out << klient.acc_nr<< std::endl;
return out;
}
std::istream& operator>> (std::fstream& in, bank& klient)
{
in >> klient.emri;
in >> klient.mbiemri;
in >> klient.acc_nr;
return in;
}
std::istream &read(bank &klient, std::istream &is)
{
std::cout << "Jepni emrin: ";
is >> klient.emri;
std::cout << "Jepni mbiemrin: ";
is >> klient.mbiemri;
std::cout << "Jep numrin e akaundit: ";
is >> klient.acc_nr;
return is;
}
const std::ostream &print(bank klient, std::ostream &os)
{
std::cout << "Emri: ";
os << klient.emri;
std::cout << "\nMbiemri: ";
os << klient.mbiemri;
std::cout << "\nNumri i akaundit: ";
os << klient.acc_nr;
return os;
}
void read_infile(bank &klient)
{
read(klient, std::cin);
outfile.open("C:\\publik\\sample.dat", std::ios::app| std::ios::binary);
outfile.write(reinterpret_cast <char *>(&klient), sizeof(bank));
outfile.close();
}
void print_outfile(bank klient)
{
infile.open("C:\\publik\\sample.dat", std::ios::in| std::ios::binary);
infile.read(reinterpret_cast <char *>(&klient), sizeof(bank));
infile.close();
}
void kerko(std::string emri, std::streampos& a)
{
bank temp;
inout.open("C:\\publik\\sample.dat", std::ios::in | std::ios::out | std::ios::binary);
while (inout.read(reinterpret_cast <char *>(&temp), sizeof(bank)))
{
if (emri == temp.emri) // emri = name.
{
print(temp, std::cout);
a = inout.tellg();
inout.close();
break;
}
}
}
int main()
{
bank klient;
bank temp2;
std::string emri;
std::streampos a;
std::cin >> emri;
bank temp;
kerko(emri,a);
std::cout <<std::endl;
system("pause");
return 0;
}
The code reads a name, searches through the file and then displays its information. The searching and displaying are successful but I get this error
Unhandled exception at 0x77CFDF58 (msvcp120d.dll) in Banke.exe:
0xC0000005: Access violation reading location 0x00DCAB9C.
The code does not execute past the print function call in the while loop.
Thank you for your time.
There are several things that are wrong here.
First of all you cannot do this:
read(klient, std::cin);
outfile.open("C:\\publik\\sample.dat", std::ios::app| std::ios::binary);
outfile.write(reinterpret_cast <char *>(&klient), sizeof(bank));
your &klient is a complex structure, using a type (std::string) of which you know nothing about its internal layout. It may have pointers pointing 'outside' of it. To read a record you should so something like this;
bank klient ;
std::ifstream istream("name", std::ios::app| std::ios::binary); // or use open
istream >> klient ;
And here we get to second point. This definition is quite ok (I would have used std::ostream instead of std::ofstream but that's a minor point)
std::ostream& operator<< (std::ofstream& out, bank& klient)
{
out << klient.emri << std::endl;;
out << klient.mbiemri << std::endl;
out << klient.acc_nr << std::endl;
return out;
}
The main point is that when you want to read emri or mbiemri how do you know where the first ends, and the second starts? The simplest way could be insert a space. It works only if emri/mbiemri do not contain spaces themselves, but for a beginner could be a good starting point.
And that's all for now, you have enough to think about...

How to parse content of a file and load into the map

i want to parse the content of a file and load into a map.
This is the file content format:
Movie-name release-year price cast ishd
"DDLG" 2010 20.00 "shahrukh and Kajal" true
"Aamir" 2008 10.00 "abc, xyz and ijkl" false
Key for map will be the first word (Movie name).
class defition:
class movieInfo
{
private:
int releaseYear;
double price;
string cast;
bool isHD;
};
This is the function i am trying to implement.
void fill_map_contents (map <string, movieInfo*> &mymap, ifstream& myfile)
{
string line;
string word;
while (getline(myfile, line))
{
out << "inside while loop " << line << endl;
stringstream tokenizer;
tokenizer << line;
movieInfo *temp = new movieInfo;
while (tokenizer >> word)
{
cout << " printing word :->"<< word << endl;
//temp->releaseYear = atoi(word);
//temp->price = 12.34;
//temp->cast = "sahahrukh salman";
//temp->isHD = false;
mymap[word] = temp;
}
delete temp;
}
}
I am not getting any idea, after while (tokenizer >> word), how to fill object variable and assign it to map.
Any help will be highly appreciated.
Devesh
cout << " printing word :->"<< word << endl;
//temp->releaseYear = atoi(word);
//temp->price = 12.34;
//temp->cast = "sahahrukh salman";
//temp->isHD = false;
in above code you are trying to access private members of the class directly which is not possible.Hence ,better solution is you should include public getter/setter for each variable as follows.
public:
void setreleaseYear(int sry){releaseYear=sry;}
void setprice(double pr){price=pr;}
void setcast(string cast){string=str;}
void setisHD(bool id){isHD=id;}
now use in place of commented code :
//temp->releaseYear = atoi(word);
temp->setreleaseYear(atoi(word));
tokenizer >> word;
//temp->price = 12.34;
temp->setprice(atof(word));
tokenizer >> word;
//temp->cast = "sahahrukh salman";
temp->setcast(word);
tokenizer >> word;
//temp->isHD = false;
temp->setisHD(word);
No need of while loop.
You must simplify things. I suggest to add inserction and extraction operator for movieinfo and choose new line as field separator
DDLG
2010
20.00
shahrukh and Kajal
true
Aamir
2008
10.00
abc, xyz and ijkl
false
class movieInfo
{
public:
int releaseYear;
double price;
string cast;
bool isHD;
friend std::ostream& operator << ( std::ostream& os, const movieinfo& i )
{
return os << i.releaseYear << '\n'
<< i.price << '\n'
<< i.cast << '\n'
<< std::boolalpha << i.isHD() << '\n';
}
friend std::istream& operator >> ( std::istream& is, movieinfo& i )
{
is >> i.releaseYear
>> i.price;
getline( is, i.cast );
return is >> std::boolalpha >> i.isHD;
}
};
void fill_map_contents (map <string, movieInfo> &mymap, ifstream& myfile)
{
while ( !myfile.eof )
{
string name;
myfile >> name;
movieInfo mi;
myfile >> m1;
mymap[ name ] = movieInfo;
}
}
note that I changed map <string, movieInfo*> in map <string, movieInfo> prefer using move semantic.
Another suggestion I will change moveinfo in:
class movieInfo
{
public:
// ctor and move, assign operator and move operator
int releaseYear() const { return mReleaseYear; };
double price() const { return mPrice; };
const string& cast() const { return mCast; };
bool isHD() const { return mIsHD; };
private:
int mReleaseYear;
double mPrice;
string mCast;
bool mIsHD;
friend std::ostream& operator << ( std::ostream& os, const movieinfo& i )
{
return os << i.releaseYear() << '\n'
<< i.price() << '\n'
<< i.cast() << '\n'
<< std::boolalpha << i.isHD() << '\n';
}
friend std::istream& operator >> ( std::istream& is, movieinfo& i )
{
is >> i.mReleaseYear
>> i.mPrice;
getline( is, i.mCast );
return is >> i.mIsHD;
}
};
You are effectively trying to parse a CSV file, with space as separator and " as quote character.
I'd recommend using a library for this, like this one.
Example code (taken from the help page):
// Note: I changed mymap to map<string, movieInfo> without a pointer - it's not
// needed
const char field_terminator = ' '; // Use a space
const char line_terminator = '\n'; // Use line break
const char enclosure_char = '"'; // Use "
csv_parser file_parser;
file_parser.set_skip_lines(1);
file_parser.init(filename);
file_parser.set_enclosed_char(enclosure_char, ENCLOSURE_OPTIONAL);
file_parser.set_field_term_char(field_terminator);
file_parser.set_line_term_char(line_terminator);
while(file_parser.has_more_rows()) {
csv_row row = file_parser.get_row();
movieInfo temp; // No need for pointers
temp->releaseYear = atoi(row[1]); // C++11: Use std::stoi()
temp->price = atof(row[2]); // C++11: Use std::stof()
temp->cast = row[3];
temp->isHD = row[4].compare("true") == 0;
mymap[row[0]] = temp;
}

setw within a function to return an ostream

here is my function
ostream margain(std::string firstWord)
{
ostream x;
x << std::setw(20) << firstWord;
return x;
}
in main I want to use the function as follow
std::cout<< margain("start") << "````````````````````````````````````" << std::endl;
// print things out
//then
std::cout<< margain("End") << "````````````````````````````````````" << std::endl;
I get the output, start or end is not shown, and the return value is
0````````````````````````````````````
how can I fix it? and why?
Edit:
I know that the function is what causing that, because if I add this
cout << std::setw(20) << firstWord;
in the function, It prints right,
I fixed it, not the best way, but as
calling the function as
margain(std::cout, "End") <<
"~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~" << endl;
the function looks like
ostream& margain(ostream& stream, std::string firstWord)
{
stream << std::left << std::setw(10) << firstWord;
return stream;
}
anyone know better way?
You are printing the value of the ostream, not value of firstword. The ostream x in this case is an unopened stream, so it doesn't "do" anything. Because the compiler allows to conversion to either bool (C++11) or void * (before C++11), the "value" from that conversion is printed. Note that any operations on x will not affect cout.
The easiest solution, I would think is to actually add std::setw(20) to your output line:
std::cout<< std::setw(20 << "End" << "````````````````````````````````````" << std::endl;
The other choice would be to pass the std::cout to margain, and return the std::string, something like this:
std::string margain(ostream& x, const std::string& firstWord)
{
x << std::setw(20);
return firstWord;
}
then you could do:
std::cout<< margain(cout, "start") << "````````````````````````````````````" << std::endl;
But it's not exactly flexible or "neat".
The third option is of course to have a MarginString class:
class MarignString
{
private:
int margin;
std::string str;
public:
MarginString(int margin, std::string str) margin(margin), str(str) {}
operator std::string() { return str; }
friend std::ostream& operator(std::ostream& os, const MarginString& ms);
};
std::ostream& operator(std::ostream& os, const MarginString& ms)
{
os << std::setw(ms.margin) << ms.str;
return os;
}
...
std::cout<< MarginString(20, "start") << "````````````````````````````````````" << std::endl;
Note that this last way is probably not that great either... ;)
struct margin
{
margin(std::string word) : word(word) { }
friend std::ostream& operator <<(std::ostream& os, margin const& m)
{
return os << std::setw(20) << m.word;
}
private:
std::string word;
};

C++ Using classes with boost::lexical_cast

I want to use my Test class with boost::lexical_cast. I have overloaded operator<< and operator>> but It gives me runtime error.
Here is my code:
#include <iostream>
#include <boost/lexical_cast.hpp>
using namespace std;
class Test {
int a, b;
public:
Test() { }
Test(const Test &test) {
a = test.a;
b = test.b;
}
~Test() { }
void print() {
cout << "A = " << a << endl;
cout << "B = " << b << endl;
}
friend istream& operator>> (istream &input, Test &test) {
input >> test.a >> test.b;
return input;
}
friend ostream& operator<< (ostream &output, const Test &test) {
output << test.a << test.b;
return output;
}
};
int main() {
try {
Test test = boost::lexical_cast<Test>("10 2");
} catch(std::exception &e) {
cout << e.what() << endl;
}
return 0;
}
Output:
bad lexical cast: source type value could not be interpreted as target
Btw I'm using Visual Studio 2010 But I've tried Fedora 16 with g++ and got the same result!
Your problem comes from the fact that boost::lexical_cast does not ignore whitespaces in the input (it unsets the skipws flag of the input stream).
The solution is to either set the flag yourself in your extraction operator, or just skip one character. Indeed, the extraction operator should mirror the insertion operator: since you explicitely put a space when outputting a Test instance, you should explicitely read the space when extracting an instance.
This thread discusses the subject, and the recommended solution is to do the following:
friend std::istream& operator>>(std::istream &input, Test &test)
{
input >> test.a;
if((input.flags() & std::ios_base::skipws) == 0)
{
char whitespace;
input >> whitespace;
}
return input >> test.b;
}