3

I am trying to overload both cout from iostream and fout from fstream

ostream& operator<<(ostream& cout, const Object& obj);

ofstream& operator<<(ofstream& fout, const Object& obj);

I want the first function to work with the console so have text for the user while I want the second function to only output the value of the variables onto a file.

However, in

void save_data(const Object& obj)
{
    fstream fout("DataBase.txt", ios::out);

    if (fout.is_open())
    {
        fout << obj;
        fout.close();
    }

    else
        cout << "DataBase.txt could not be saved!" << endl;
}

I have text that I used to guide the user inside my function that overloads cout in my file.

Ratul
  • 41
  • 2

2 Answers2

4

std::fstream inherits from std::iostream, which in turn inherits from std::ostream. There's no std::ofstream in that chain, making that overload an unsuitable candidate.

One way to fix this is to use std::ofstream at the callsite instead of std::fstream. You can also add an overload for std::fstream. However, be aware that std::ostream is not necessarily the console; that's a guarantee you have to provide yourself from the rest of the code. For example, a std::ofstream could be upcast to std::ostream and then used to print an Object and your overload set would assume this stream is for the console. It might be hard to ensure the rest of your code provides that guarantee. In addition, robust detection of a console is going to be platform-dependent with its own answers elsewhere on the site (e.g., for Windows).

One way that existing programs handle this requirement is to have an explicit "interactive" option on the command line. For example, git commands use --interactive and sometimes -i for this purpose. This way, the user asks for the extra guiding output and there's no clever detection trickery required.

chris
  • 60,560
  • 13
  • 143
  • 205
  • Can't he just go with a single `std::ostream` overload? – David G Nov 04 '19 at 05:26
  • @0x499602D2, As far as I understand the requirements, these overloads have different outputs. If changing the strategy to one that chooses console vs. file without relying on the static type of the stream, then one overload should do fine, though it would still have branching inside to cover both output destinations. – chris Nov 04 '19 at 13:15
  • In both overloads they are writing to the stream object and only that, correct? Then there should be only one overload. – David G Nov 04 '19 at 18:55
  • @0x499602D2, No, the OP wants extra output when it's sent to the console. "I want the first function to work with the console so have text for the user while I want the second function to only output the value of the variables onto a file." This is my interpretation, which is supported by "guide the user" later in the question. – chris Nov 04 '19 at 19:02
0

Thanks to the help from @chris I was able to figure out what the real problem was. Normally one doesn't need to overload based on the base and derived I/O stream types. There's usually a polymorphic way to do what you want. In this case you can check if the stream being written to is std::cout and construct a new stream with a buffer to write to it.

std::ostream& operator<<(std::ostream& os, Obj const& obj) {
  std::ostream o(nullptr);
  if (&os == &std::cout) {
    o.rdbuf(std::cout.rdbuf());
  }
  o << "hello\n";
  return os;
}

Writing to o will do nothing if the buffer is a null pointer. This will allow you to write your console output guides for the user without separating both implementations.

If you would still like to split the implementations, create two functions and call the appropriate one when the above if condition is true and false.

std::ostream& write_with_console_o(std::ostream&);
std::ostream& write_with_file_o(std::ostream&);

std::ostream& operator<<(std::ostream& os, Obj const& obj) {
  if (&os == &std::cout) {
    return write_with_console_o(os);
  }
  return write_with_file_o(os);
}
David G
  • 94,763
  • 41
  • 167
  • 253