3

The following (unsafe) code works

#include <iostream>
#include <fstream>

std::ofstream* create_unsafe_stream(const char* filename) {
    return new std::ofstream {filename};
}

int main () {
    std::ofstream& u_out = *create_unsafe_stream("foo.txt");
    u_out << "foo\n" ;
    delete &u_out;
    return 0;
}

I tried to produce a safer version

#include <iostream>
#include <fstream>
#include <memory>

using stream_ptr = std::unique_ptr<std::ostream>;

stream_ptr create_safe_stream(const char* filename) {
    return stream_ptr{ new std::ofstream {filename}};
}

int main() {
  std::ostream& s_out = *create_safe_stream("foo.txt");
  s_out << "foo\n" << std::endl; 
  return 0
}

Which compiles but, when I run it give me a segmentation fault. I was thinking that the problem is caused by the unique_ptr going out of the scope. So I tried to modify a bit the main to

int main() {
   stream_ptr ofile = create_safe_stream("foo.txt");
   std::ostream& s_out = *ofile;
   s_out << "foo\n"; 
}

which works again.

QUESTION

Is there a way not to use an 'intermediate' variable like ofile and doing all in a line?

EDIT

The function create_safe_stream is a toy model of what I want, that is this function may return either the std::ofstream to that file or the std::cout, so I think I really need to return a pointer to the base class std::ostream. How can I do?

MaPo
  • 613
  • 4
  • 9
  • (After the edit): `ofile` is not intermediate, it's the OWNER of the object. Rather `s_out` is a helper alias to the contained object. – Eljay Jun 14 '19 at 19:39

1 Answers1

3

Your assumption is correct. create_safe_stream returns a std::unique_ptr which immediately goes out of scope and thus the raw resource it holds is deleted and trying to use it is UB.

The way to not use an intermediate variable and doing it all in one line is by just returning a std::ofstream object instead:

std::ofstream create_safe_stream(const char* filename) {
    return std::ofstream {filename};
}

int main() {
  std::ostream s_out = create_safe_stream("foo.txt");
  s_out << "foo\n" << std::endl; 
  return 0;
}
Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
  • Thank you for the answer. However what I wrote was a 'toy model' of my problem. Actually the function `create_safe_stream` should either open a file or redirect `std::out` (as in https://stackoverflow.com/questions/56521318/ostream-class-that-outputs-either-on-cout-or-on-a-file), therefore I think that I really need to return a pointer to `std::ostream`. Is there a way to do it anyhow? – MaPo Jun 14 '19 at 19:12
  • You’d still have to hold onto the smart pointer, but one trick you can do in that case is return a `std::unique_ptr>` where you either have the deleted be `[](std::ostream* os) { delete os; }` or if you are returning a pointer to `std::cout`, the deleter is a nop. (For optimal performance, don’t use `std::function`, but hopefully you get the idea.) – Ben Jun 14 '19 at 23:54