Keep a stream open from a function to another - c++

Is there any way to keep a stream (to read or write in a file) open from a function to another in C++?

Yes, you can either create the stream outside of the functions and pass it as a parameter to the methods:
void myFunction(ifstream &stream) {...}
Later close the stream when you are done with it: stream.close().
Or create the stream within the first function and return it to the calling method and then pass it to the second function.

Pass it by reference
void myFunction(ifstream &myStream)

Make it global or pass it as an argument but ensure that if you pass it as an argument you past it by reference not by value! If you pass it by value the compiler will NOT complain and weird things start happening.

Since C++11 file stream got move constructor (6). You can use it to pass opened stream between functions. Consider the following code snippet:
#include <iostream>
#include <fstream>
bool open_stream(const std::wstring& filepath, std::ifstream& stream)
{
std::ifstream innerStream;
innerStream.open(filepath.c_str(), std::ios::in | std::ios::binary);
if (innerStream.is_open())
{
stream = std::move(innerStream); // <-- Opened stream state moved to 'stream' variable
return true;
}
return false;
} // <-- innerStream is destructed, but opened stream state is preserved as it was moved to 'stream' variable
Consider the next code to illustrate the usage of open_stream:
int main()
{
std::ifstream outerStream;
std::wcout << L"outerStream is opened: " << outerStream.is_open() << std::endl; // <-- outerStream is opened: 0
if (!open_stream(L"c:\\temp\\test_file.txt", outerStream))
{
return 1;
}
std::wcout << L"outerStream is opened: " << outerStream.is_open() << std::endl; // <-- outerStream is opened: 1
// outerStream is opened and ready for reading here
return 0;
}

Related

How to redirect std::cout to file conditionally

In my code I have a a function that takes as argument a boolean variable. If this variable is true then the output shall be redirected to a file. If not, then to std::cout
The code looks like this and Its been inspired by a relevant question [1]
void MyClass::PPrint(bool ToFile)
{
std::streambuf *coutBufferBak = std::cout.rdbuf();
std::ofstream out("out.txt");
if (ToFile) { std::cout.rdbuf(out.rdbuf()); }
std::cout << "Will I be written to a file or to cout ?\n";
if (ToFile) {std::cout.rdbuf(coutBufferBak);} // reset cout
}
But in the case that the ToFile flag is false the file will be generated nonetheless and it will be empty. Is there a way to do it so that the file won't be generated ? If for example I try to include on the first IF statement the std::ofstream out("out.txt"); then I will get a SegFault due to the the variable scope being limited to that if.
Don't redirect std::cout. Write your print in terms of a std::ostream & parameter, and choose an appropriate std::ostream to pass.
void MyClass::PPrintImpl(std::ostream & out)
{
out << "Will I be written to a file or to cout ?\n";
}
// a.k.a
std::ostream & operator <<(std::ostream & out, const MyClass &)
{
return out << "Will I be written to a file or to cout ?\n";
}
void MyClass::PPrint(bool ToFile) {
if (ToFile) {
std::ofstream fout("out.txt");
PPrintImpl(fout);
} else {
PPrintImpl(std::cout);
}
}
We pass std::ostreams by reference because we the identity, not just the value of the stream object matters. We know this because they aren't copyable (the copy constructor is deleted)
You can open the file conditionally:
std::ofstream out;
if(...)
out.open("out.txt");

overwrite file only at the first actual write

I would like to open a file for writing, such that the file is wiped and written over at the first write instruction. That is, the file should retain its contents when opened, and only at the first insertion is actually written over.
Using std::fstream, if I use the mode std::ios_base::out, the file is immediately wiped as soon as the constructor is called
#include <fstream>
int main()
{
std::fstream f("test.txt", std::ios_base::out);
// Now test.txt is empty
f << "hello";
}
This behavior would be ok if test.txt does not exists. But if test.txt exists, I would like to have its contents preserved until I write to it. If I open it with
std::fstream f("test.txt", std::ios_base::out | std::ios_base::in);
indeed an existing file is still preserved, and it is written over only on f << "hello".
However, if the file exists and contains more data then the string "hello", then f << "hello" will only overwrite the first 5 characters. I would like the file to be wiped, as if it were opened with std::ios_base::out, but only when I execute f << "hello".
Context
The problem arises with a routine like this:
void do_something(std::ostream& s)
{
//... very long computation
s << "some infos" << std::endl;
}
I call do_something passing to it a stream, which could or could not be a file. If it is a file, I would like to be able to inspect test.txt before the computation is done.
One possibility could be to wrap a fstream in a new object that actually opens the file only when operator<< is called, something like
#include <fstream>
class myfstream {
std::fstream m_f;
std::string m_filename;
public:
myfstream(std::string& filename) : m_filename{filename} { }
template <typename U>
myfstream& operator<<(const U& s) { if (!m_f.is_open()) m_f.open(m_filename, std::ios_base::out); m_f << s; return *this; }
};
The drawback of this solution is the lack of flexibility: if the routine do_something does something else than just calling operator<<, one has to define all the other members of std::ostream, to mimick its behavior. Also, to retain flexibility do_something has to be declared as
template <typename S>
void do_something(S& o);
Is there a better solution? Would it be possible, say, given a fstream to delete all data after the current position of the output indicator?
Update
Apparently, a possible solution is to get the actual write position with tellp() and after closing the file, resize it. See following code:
#include <fstream>
#include <filesystem>
#include <iostream>
int main()
{
typename std::fstream::pos_type size;
{
char d;
std::fstream f("test.txt", std::ios_base::in | std::ios_base::out);
std::cin >> d; // Just to pause
f << "hello";
size = f.tellp();
}
char d;
std::cin >> d; // Just to pause
std::filesystem::resize_file("test.txt", size);
return 0;
}
(The std::cin are there only to pause and be able to check the actual contents of the file).
Testing the program, after the first "pause" the file is still intact. After the second pause, the first characters have been overwritten with the string "hello", but any text following that is still untouched. Finally, resize_file shrinks the file to the actual length of the written string.
The above code does an implicit conversion between a pos_type as returned from tellp() and a std::uintmax_t as required from resize_file.
Is this conversion safe?

Copy all std::cout messages to log file? [duplicate]

I need redirect the copy of std::cout to the file. I.e. I need see the output in console, and in file. If I use this:
// redirecting cout's output
#include <iostream>
#include <fstream>
using namespace std;
int main () {
streambuf *psbuf, *backup;
ofstream filestr;
filestr.open ("c:\\temp\\test.txt");
backup = cout.rdbuf(); // back up cout's streambuf
psbuf = filestr.rdbuf(); // get file's streambuf
cout.rdbuf(psbuf); // assign streambuf to cout
cout << "This is written to the file";
cout.rdbuf(backup); // restore cout's original streambuf
filestr.close();
return 0;
}
then I write string to the file, but I see the nothing in console. How can I do it?
The simplest you can do is create an output stream class that does this:
#include <iostream>
#include <fstream>
class my_ostream
{
public:
my_ostream() : my_fstream("some_file.txt") {}; // check if opening file succeeded!!
// for regular output of variables and stuff
template<typename T> my_ostream& operator<<(const T& something)
{
std::cout << something;
my_fstream << something;
return *this;
}
// for manipulators like std::endl
typedef std::ostream& (*stream_function)(std::ostream&);
my_ostream& operator<<(stream_function func)
{
func(std::cout);
func(my_fstream);
return *this;
}
private:
std::ofstream my_fstream;
};
See this ideone link for this code in action: http://ideone.com/T5Cy1M
I can't currently check if the file output is done correctly though it shouldn't be a problem.
You could also use boost::iostreams::tee_device. See C++ "hello world" Boost tee example program for an example.
Your code does not work, because it is the streambuf that determines where the output written to a stream end up, not the stream itself.
C++ does not have any streams or streambufs that support directing the output to multiple destinations, but you could write one yourself.

functions to write information to a file in C++

I have been working on a project for school, and I have run into an issue. I am trying to avoid hard coding everything in my program, and part of my requirement is to use fstream. Here is what is throwing an error. I am using G++ as my compiler.
void order::printToFile(string file)
{
ofstream output;
try
{
output.open(file, ios::app);
}
catch(...)
{
cerr << "An error has occurred";
}
output << this->info.orderID << setw(10) << this->info.type << setw(10) << this->info.quantity << setw(10) << this->info.zip << setw(10) << (this->info.shipCost + this->info.wholesale) << setw(10) << this->info.profit << endl << endl;
output.close();
}
It is giving me the following error:
No matching function to call for 'std::basic ofstream<char>::open( std::string&, const openmode&)'
Can someone give me a hand with this, please? Thanks
No matching function to call for 'std::basic ofstream<char>::open( std::string&, const openmode&)'
A "no matching function" error means that the compiler searched for but could not find an overload that matched the arguments provided at the call site. open() prior to C++11 had one overload which took a buffer of type char const*. This has been updated and in addition to the first overload, open() now supports an argument of type std::string const&.
The problem must be then that your compiler is not C++11 aware. Adding -std=c++11 to the command line should fix the problem. On the other hand, if you can't do so, you can always grab a pointer to the buffer using c_str():
output.open(file.c_str(), ios::app);
// ^^^^^^^^
Another thing you should know is that IOStreams are designed by default to not throw exceptions. Instead, they reflect stream errors in the form of a bitmask type known as the "stream state". It can be accessed by using the boolean operator method streams support.
You can enable exceptions by setting the appropriate bits in the exceptions() mask, but I wouldn't recommend it for such a simple example. Simply checking the stream after opening it should suffice:
if (std::ofstream output(file.c_str(), std::ios_base::app)) {
output << "...";
}
else {
std::cerr << "An error has occurred.";
}
And lastly, streams do not need to be manually closed. When the scope in which they are defined end their destructors will be called which frees the file resource automatically. Calling close() is only needed in cases where you wish to see if it will work, or if you no longer need the file and want output flushed immediately.
Maybe your compiler is not C++11. Try changing
output.open(file, ios::app);
to
output.open(file.c_str(), ios::app);
The ofstream constructor that takes an std::string argument was added in C++11. Assuming your compiler supports it, you need to enable C++11 mode (-std=c++11 for gcc and clang). Otherwise, change the function call to:
output.open(file.c_str(), ios::app);
Also, note that if ofstream fails to open a file, it will not result in an exception, unless you explicitly enable exceptions.
output.exceptions(std::ofstream::failbit);
output.open(file.c_str(), ios::app); // will throw exception if open fails
The other option is to open the file, and then check if it succeeded
output.open(file.c_str(), ios::app);
if(!output) {
// error occurred, handle it
}
This worked for me with g++ version 4.8.2 on Ubuntu 14.04.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void printToFile (string fname) {
ofstream output;
try {
// NOTE: passing fname.c_str() not just fname
output.open (fname.c_str(), ios::app);
} catch (std::exception e) {
cout << "err occurred: " << e.what() << endl;
}
output << "foo bar baz" << endl;
output.close ();
}
int main () {
printToFile ("foo.txt");
return 0;
}
I believe your problem is that you're attempting to call open with a std::string instead of a char * as the first arg. See the comment in the code above.

Strange behavior of stringstream passed by reference

For a project, I'd like to use stringstream to carry on data. To achieve this goal, I have to pass some stringstream as parameter to some function, but when I output the stringstreams, I see something like an address.
The code :
#include <iostream>
#include <sstream>
void doStuff(const std::iostream& msg)
{
std::cerr << msg << std::endl;
}
int main(void)
{
doStuff(std::stringstream("av"));
}
The output is :
0xbff4eb40
Can someone explains why I get an address when passing an rvalue ?
And why can't I pass a stringstream by value ?
You probably want to access the string on which the stringstream is storing its data:
void doStuff(const std::stringstream& msg)
{
std::cerr << msg.str() << std::endl;
}
What is happening in your code is that iostreams contain a void* operator which returns 0 if the stream contains any error or has reached EOF, and another value otherwise. This is usefull for error checking.
When you try to write you stream to std::cerr, the compiler realizes that the stream can be converted to a void* using that operator, and that a void* can be written to a ostream(the operator<< has been defined), and therefore uses it.
Note that i changed the method's signature so that it receives an std::stringstream as an argument, since std::iostream::str is not defined(this method is only available on string streams).
You get an address because it (like other streams) has a conversion to void * (which is primarily useful as a Boolean, to see whether reading/writing the stream has failed).
You can't pass it by value, because streams (again, in general, not just stringstreams) don't support copying and/or assigning.
To print the content of the stream, you could do something like:
void dostuff(std::iostream &msg) {
std::cerr << msg.rdbuf() << "\n";
}
Edit: Here's a complete demo program:
#include <iostream>
#include <sstream>
void show(std::ostream &os) {
std::cout << os.rdbuf() << "\n";
}
int main(){
std::stringstream test("whatever");
show(test);
return 0;
}
When I execute it, the output I get is the expected "whatever".