0

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.

ex1led
  • 427
  • 5
  • 21
  • 2
    Why can't you simply defer the opening/creation of `out.txt` until the body of the `if (ToFile)` conditional? – G.M. Mar 15 '21 at 12:16
  • 4
    you need not redirect `std::cout` when you can just choose between `std::cout` and a file stream – 463035818_is_not_an_ai Mar 15 '21 at 12:16
  • It looks like you should wrap the `rdbuf` logic into a class. This gives you RAII behavior; the dtor will automatically reset `cout`. You can then use a `std::optional<>` wrapper to handle the conditional part. – MSalters Mar 15 '21 at 12:28
  • FWIW, I would change the function to `void MyClass::PPrint(ostream& out)`, then the function body just becomes `out << sutff_to_output`. This allows users of the function to print to any stream, and also the ability to pick a different file location. – NathanOliver Mar 15 '21 at 12:35

2 Answers2

3

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)

Caleth
  • 52,200
  • 2
  • 44
  • 75
  • Clean programming wise, this seems to me the best practice. Thank you. I've been quite struggling with ```c++``` and this seems very handy. Could you elaborate maybe on why the ```operator ``` is needed in this case if possible? And why the ```ostreams``` must be passed as refferences to the functions.Thanks again. – ex1led Mar 15 '21 at 13:00
  • 1
    @Broxigar It isn't *needed*. It's just that the conventional name of a "write to output stream" function is `operator <<` – Caleth Mar 15 '21 at 13:02
2

You can open the file conditionally:

std::ofstream out;
if(...)
    out.open("out.txt");
Daniel
  • 30,896
  • 18
  • 85
  • 139