Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Obtain a std::ostream either from std::cout or std::ofstream(file)

how do I bind a std::ostream to either std::cout or to an std::ofstream object, depending on a certain program condition? Although this invalid for many reasons, I would like to achieve something that is semantically equivalent to the following:

std::ostream out = condition ? &std::cout : std::ofstream(filename); 

I've seen some examples that are not exception-safe, such as one from http://www2.roguewave.com/support/docs/sourcepro/edition9/html/stdlibug/34-2.html:

int main(int argc, char *argv[]) {   std::ostream* fp;                                           //1   if (argc > 1)      fp = new std::ofstream(argv[1]);                         //2   else      fp = &std::cout                                          //3    *fp << "Hello world!" << std::endl;                         //4   if (fp!=&std::cout)       delete fp; } 

Does anyone know a better, exception-safe solution?

like image 970
mavam Avatar asked Dec 14 '08 20:12

mavam


People also ask

What is std :: ostream?

The std::ostream , the std::istream or the std::iostream are base classes of stream types (e.g. std::stringstream , std::fstream , etc.) in the Standard Library. These classes are protected against instantiation, you can instantiate their derived classes only.

Is ofstream an Ostream?

ofstream is an output file stream. It is a special kind of ostream that writes data out to a data file.

Is cout an ofstream?

Yes it is. The original code has a memory leak if an exception is thrown while processing (*fp << "Hello World" << std::endl).

How do you use std cout?

std::cout is used to output a value (cout = character output) std::cin is used to get an input value (cin = character input) << is used with std::cout, and shows the direction that data is moving (if std::cout represents the console, the output data is moving from the variable to the console).


2 Answers

std::streambuf * buf; std::ofstream of;  if(!condition) {     of.open("file.txt");     buf = of.rdbuf(); } else {     buf = std::cout.rdbuf(); }  std::ostream out(buf); 

That associates the underlying streambuf of either cout or the output file stream to out. After that you can write to "out" and it will end up in the right destination. If you just want that everything going to std::cout goes into a file, you can aswell do

std::ofstream file("file.txt"); std::streambuf * old = std::cout.rdbuf(file.rdbuf()); // do here output to std::cout std::cout.rdbuf(old); // restore 

This second method has the drawback that it's not exception safe. You possibly want to write a class that does this using RAII:

struct opiped {     opiped(std::streambuf * buf, std::ostream & os)     :os(os), old_buf(os.rdbuf(buf)) { }     ~opiped() { os.rdbuf(old_buf); }      std::ostream& os;     std::streambuf * old_buf; };  int main() {     // or: std::filebuf of;      //     of.open("file.txt", std::ios_base::out);     std::ofstream of("file.txt");     {         // or: opiped raii(&of, std::cout);         opiped raii(of.rdbuf(), std::cout);         std::cout << "going into file" << std::endl;     }     std::cout << "going on screen" << std::endl; } 

Now, whatever happens, std::cout is in clean state.

like image 148
Johannes Schaub - litb Avatar answered Sep 21 '22 10:09

Johannes Schaub - litb


This is exception-safe:

void process(std::ostream &os);  int main(int argc, char *argv[]) {     std::ostream* fp = &cout;     std::ofstream fout;     if (argc > 1) {         fout.open(argv[1]);         fp = &fout;     }     process(*fp); } 

Edit: Herb Sutter has addressed this in the article Switching Streams (Guru of the Week).

like image 41
Tom Avatar answered Sep 20 '22 10:09

Tom