printing to file with functions?

Is it possible to use a function to pass data to a out stream to put it into a file?

I have a return name function, which simply returns the name that I want to print to the file. (I also have other functions that will return other information, but just now wanting name to work). And I have a function that writes to the file. Both below, but I get an error with the stream << saying that the "no operator << matches these operands". Is this nor possible to do in this way, do I need to overload the << (I have never overloaded before, but I do know about it)

Also, if it does not work this way, why? Why does it not just return the string object and print that to the file?
1
2
3
4
  string book::getName() const
{
	return name;
}


1
2
3
4
5
6
7
8
9
void book::writeToFile(const book &obj) const
{	
	if (!myOutStream)
	{
		cout << "Error opening out file\n";
	}

	myOutStream << obj.getName();
}
I can't exactly understand the issue from the code shown alone, but if I understand you correctly, what you need is to overload the << operator for an out stream and a book.

1
2
3
4
std::ostream& operator<<(std::ostream& os, const book& book)
{
    return os << book.getName();
}


Now, on the calling side, you can use it like:

1
2
book book;
cout << book << '\n';
I have overloaded the << operator as a friend function. But I seem to still be getting the same issue, even though the operands are correct from that I can see. I want to access the overloaded operator from within another function in the cpp (shown below).

Where I am getting the issue is myOutStream << obj; and it is saying "no operator << matches these operands" But surely since myOutStream is an ostream, that is correct for the overloaded function, and then so is the obj object, surely that is the same as the book &obj in the operator overloaded function?

1
2
3
4
5
6
7
8
9
ostream& operator<<(ostream& stream, const book &obj)
{
	stream << obj.getName();
	stream << obj.getPrice();
	stream << obj.getISBN();
	stream << obj.getCopiesAvail();
	stream << "-----------------------------\n";
}


1
2
3
4
5
6
7
8
9
void book::writeToFile(const book &obj) const
{	
	if (!myOutStream)
	{
		cout << "Error opening out file\n";
	}

	myOutStream << obj;
}



This is how i declared the stream if important

 
ofstream myOutStream{ "Books.txt", ios::app, ios::binary };
Last edited on
 
myOutStream << obj.getName();


no operator << matches these operands


The compiler should also provide some info about the found operands. Does obj.getName() return a type std::string? Note that file stream insertion isn't usually used with a binary file.

If .getName(), getPrice() et al are public methods of book, then operator<< doesn't need to be declared as a friend function.

1
2
3
4
5
6
7
8
9
10
ostream& operator<<(ostream& stream, const book &obj)
{
	stream << obj.getName();
	stream << obj.getPrice();
	stream << obj.getISBN();
	stream << obj.getCopiesAvail();
	stream << "-----------------------------\n";

        return stream;  // <<-- NOTE
}


If the issue is still present,will you post a complete example showing the problem.
Last edited on
I only just added binary to test if that was causing the issue, I never had it declared with that originally. So I will remove that.

The compiler seems to be complainig about the type of stream saying no operator found which takes a left hand operand of type const::std ofstream. Is that saying my stream should be a const?


The reason I made it a friend function, is because when I was reading about the friend functions, it said that for me to pass in the object (the book object in this case) it has to be done as the right hand operand, if i was overloading a operator not as a friend function, then the book object would be passed in as the left hand operand which is where the stream object should be. Not sure if I have explained that well.

This is my whole code

header
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#pragma once
#include <iostream>
#include <fstream>
#include <string>

using namespace std;
class book
{
private:
	string name;
	string ISBN;
	string price;
	int copiesAvail;
	ofstream myOutStream{ "Books.txt", ios::app };
	ifstream myInStream{ "Books.txt" };
public:
	book(string Name, string Isbn, string Price, int CopiesAvail) : name{ Name }, ISBN{ Isbn }, price{ Price }, copiesAvail{ CopiesAvail }
	{
		
	};

	string getName() const;
	string getISBN() const;
	string getPrice() const;
	int getCopiesAvail() const;
	void writeToFile(book const & obj) const;
	void readFromFile() const;
	friend ostream& operator<< (ostream &stream, const book &obj);
	//friend istream& operator>>  (istream &stream, const book)

};


.CPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "book.h"


ostream& operator<<(ostream& stream, const book &obj)
{
	stream << obj.getName();
	stream << obj.getPrice();
	stream << obj.getISBN();
	stream << obj.getCopiesAvail();
	stream << "-----------------------------\n";

	return stream;
}


string book::getName() const
{
	return name;
}

string book::getISBN() const
{
	return ISBN;
}

string book::getPrice() const
{
	return price;
}

int book::getCopiesAvail() const
{
	return copiesAvail;
}

void book::writeToFile(const book &obj) const
{	
	if (!myOutStream)
	{
		cout << "Error opening out file\n";
	}

	myOutStream << obj;
}

void book::readFromFile() const
{
	if (!myInStream)
	{
		cout << "Error opening in file\n";
	}

}

Hello kmce,

Some things to consider:

Book.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#ifndef BOOK_HPP  // <--- The header guard is better than "#pragma"
#define BOOK_HPP

//#include <iostream>
//#include <fstream>
//#include <string>
//
//using namespace std;  // <--- Should never be in a header file. http://www.lonecpluspluscoder.com/2012/09/22/i-dont-want-to-see-another-using-namespace-xxx-in-a-header-file-ever-again/

class Book
{
    private:
        string name;
        string ISBN;
        string price;
        int copiesAvail;
        ofstream myOutStream{ "Books.txt", ios::app };
        ifstream myInStream{ "Books.txt" };
    public:
        Book() {}  // <--- Need to provide your own default ctor.
        Book(string Name, string Isbn, string Price, int CopiesAvail) : name{ Name }, ISBN{ Isbn }, price{ Price }, copiesAvail{ CopiesAvail } {}
        //{
                 // <--- Put at the end of the above line and the (;) not needed.
        //};

        string getName() const;
        string getISBN() const;
        string getPrice() const;
        int getCopiesAvail() const;
        void writeToFile(Book const & obj) /*const*/;
        void readFromFile() const;
        friend ostream& operator<< (ostream &stream, const Book &obj);
        //friend istream& operator>>  (istream &stream, const book)
};
#endif // !BOOK_HPP 

And in Book.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

#include "Book.hpp"


void Book::writeToFile(const Book &obj) /*const*/
{
    if (!myOutStream)
    {
        cout << "Error opening out file\n";
    }

    myOutStream << obj;
}

By making the function "const" you are saying that "myOutStream" is not allowed to change, but it does need to change. Removing the "const" here and in the header file allowed the program to compile properly with no errors.

Did not have a "main" to work with, so I am not sure about testing the program.

Although it did compile you might want to remove the "const" from the "readFromFile" function as well.

Andy
no operator found which takes a left hand operand of type const::std ofstream. Is that saying my stream should be a const?

No. It says that you have code A << B; where the A is const,
but there is no operator<< ( const ostream&, something )

The reason why the A (Book::myOutStream) is const is that the myOutStream is a member of Book and the A << B; statement is within const member function of Book (which treats all members of *this as const).


Why is the ofstream a member of a Book? Every Book object will try to open the same file for writing. That makes no sense (and most likely does strange things).


Consider:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ostream& operator<< ( ostream &stream, const Book &obj )
{
  return obj.writeToFile( stream );
}

ostream& Book::writeToFile( ostream& myOut ) const
{
  myOut << copiesAvail;
  myOut << "-----------------------------\n";
  return myOut;
}

int main() {
  ofstream myOutStream{ "Books.txt", ios::app };
  if ( !myOutStream ) { cout << "Fail"; return 1; }
  Book library[42];
  for ( auto book : library ) {
    myOutStream << book;
  }
}

In that the operator<< is not a friend. It is not even necessary, because you could write in main:
1
2
3
  for ( auto book : library ) {
    book.writeToFile( myOutStream );
  }
IMO writeToFile() should be defined const, but ofstream should be defined as mutable (but note keskiverto's comment above re having ofstream as a member variable). See https://en.cppreference.com/w/cpp/language/cv

OK so I have read through all the answers and understand how to fix the issue now, so thank you all. However, I have some follow up questions, just to make sure I am fully understanding how eveything works

Firstly, regarding the header guards rather than pragma. I am learning from pluralsight and the woman doing the tutorial said she is not going to teach the header guards, and they are too confusing, and to just use pragma, should I not pay attention to this and instead look into the header guards then?

Secondly, I have looked more into const functions, and have learned that const functions can not call member functions that are not const themselves, (can not find anything on friend functions). I understand that the problem with the code was the stream trying to be changed. But is the reason it was not working originally because the stream was trying to be changed in the writeToFile function, or is it because the writeToFile function was calling a non const function(which, as i said above not sure if it can do, since it is a friend function) and that was trying to change the stream.

Thirdly, This code is just for practice so I am just thinking and testing out different ways of doing things. I understand that the best way to deal with the stream is probably the way keskiverto showed. But just thinking, would another suitable way be to declare the stream as a static variable, so that only 1 version of it would exist between all of the versions of the object?

Lastly, I just want to make sure I am understanding this correctly

The reason why the A (Book::myOutStream) is const is that the myOutStream is a member of Book and the A << B; statement is within const member function of Book (which treats all members of *this as const).


So, myOutStream is const because the function writeToFile is const. And since obj is a parameter which is a book object, and this book object was passed in as an argument it has a *this pointer, which refers to all the member variables, and in this case these are all const. And these are all const because the function the book obj is passed into is a const function?

Hello kcme,


Firstly, regarding the header guards rather than pragma. I am learning from pluralsight and the woman doing the tutorial said she is not going to teach the header guards, and they are too confusing, and to just use pragma, should I not pay attention to this and instead look into the header guards then?


Too confusing to whom? If it is the woman doing the tutorial I would say find another one.

The "#pragma" may just be a Windows directive and may not be available to all compilers.

Header guards are very simple.
1
2
3
4
5
#ifndef NAME
#define NAME


#endif // !NAME 

For me the "NAME" is highlighted and as I type a new name it changes in the other 2 places, so that everything matches.

With these 3 lines you would put you header file code in the open part. What you put for name is your choice, but capital letters seem to be used most often.

This can be used by anyone with any compiler. Whereas "#pragma" may not.

Sorry your second point is a bit beyond what I am familiar with. Mostly the "const" part.

Your third point Your way of doing this can work, but I would agree with keskiverto. It is easier to define the file stream in "main" and pass it to the function(s) that need it.

Andy
To play devil's advocate, #pragma once is supported by virtually all compilers these days.

But note the caveats section of the corresponding Wikipedia article.
https://en.wikipedia.org/wiki/Pragma_once#Caveats
There are uncommon edge cases where #pragma once could produce different results than include guards, but this only matters if you're changing the headers in the file system during compilation.
Last edited on
@Ganado,

Thanks for the input. I had an old understanding of this.

Andy
The original code was:

 
myOutStream << obj.getName();


As the function is const, the function can't directly or indirectly make any changes to a class variable that is not marked as mutable. Effectively the this pointer is pointer to const and so anything referenced from it (class variables) can't be changed. Whether a function in a particular case doesn't make a change is not relevant - if it could make a change then its counted as making a change.

In the code, the stream insertion operator << changes the contents of the stream myOutStream and as the the function is marked as const, this isn't allowed as myOutStream is not defined as mutable.

IMO, if the compiler supports it, then use #pragma. Most compilers now do. It's easier to use. However you do need to be familiar with code guards as they are still very common in code.

re static stream. Yes, if all instances of the class are to use the same file. There are issues around sync etc for multiple instances. This is why classes need careful design.


Topic archived. No new replies allowed.