How to override cout in C++?

10,548

Solution 1

Overriding the behaviour of std::cout is a really bad idea as other developers will have a hard time understanding that the use of std::cout doesn't behave as usual.

Make your intention clear with a simple class

#include <fstream>
#include <iostream>
class DualStream
{
   std::ofstream file_stream;
   bool valid_state;
   public:
      DualStream(const char* filename) // the ofstream needs a path
      :
         file_stream(filename),  // open the file stream
         valid_state(file_stream) // set the state of the DualStream according to the state of the ofstream
      {
      }
      explicit operator bool() const
      {
         return valid_state;
      }
      template <typename T>
      DualStream& operator<<(T&& t) // provide a generic operator<<
      {
         if ( !valid_state ) // if it previously was in a bad state, don't try anything
         {
            return *this;
         }
         if ( !(std::cout << t) ) // to console!
         {
            valid_state = false;
            return *this;
         }
         if ( !(file_stream << t) ) // to file!
         {
            valid_state = false;
            return *this;
         }
         return *this;
      }
};
// let's test it:
int main()
{
   DualStream ds("testfile");
   if ( (ds << 1 << "\n" << 2 << "\n") )
   {
      std::cerr << "all went fine\n";
   }
   else
   {
      std::cerr << "bad bad stream\n";
   }
}

This provides a clean interface and outputs the same for both the console and the file. You may want to add a flush method or open the file in append mode.

Solution 2

You can swap the underlying buffers. Here is that done facilitated through RAII.

#include <streambuf>
class buffer_restore
{
    std::ostream&   os;
    std::streambuf* buf;
public:
    buffer_restore(std::ostream& os) : os(os), buf(os.rdbuf())
    { }
    ~buffer_restore()
    {
        os.rdbuf(buf);
    }
};
int main()
{
    buffer_restore b(std::cout);
    std::ofstream file("file.txt");
    std::cout.rdbuf(file.rdbuf());
    // ...
}

Solution 3

I assume you have some code using std::cout and printf which you cannot modify, otherwise the most simple way to solve your problem would be to write to a different stream from cout and use fprintf rather than or in conjunction with printf.

By following that approach you could define both a new stream class that actually wrote both to standard output and to a given file, as well as a function that combined calls to both printf and fprintf.

However a much simpler approach is to use the tee program, originally from UNIX, which copies its input both to output and to a given file. With that you could simply call your program in this way:

your_program | tee your_log_file

Answers to this question lead to a few alternative implementations available for Windows. Personally I always install cygwin on my PC's to have UNIX/Linux utilities available.

Share:
10,548

Related videos on Youtube

Rasmi Ranjan Nayak
Author by

Rasmi Ranjan Nayak

Always ready for programming. Favorite Programming Languages C, C++, Python, Java, Android

Updated on June 18, 2022

Comments

  • Rasmi Ranjan Nayak
    Rasmi Ranjan Nayak 11 months

    I have a requirement, I need to use printf and cout to display the data into console and file as well. For printf I have done it but for cout I am struggling, how to do it?

       #ifdef _MSC_VER
         #define GWEN_FNULL "NUL"
         #define va_copy(d,s) ((d) = (s))
             #else
             #define GWEN_FNULL "/dev/null"
            #endif
            #include <iostream>
            #include <fstream>
            using namespace std;
            void printf (FILE *  outfile, const char * format, ...) 
            {
                va_list ap1, ap2;
                int i = 5;
                va_start(ap1, format);
                va_copy(ap2, ap1);
                vprintf(format, ap1);
                vfprintf(outfile, format, ap2);
                va_end(ap2);
                va_end(ap1);
            }
        /*    void COUT(const char* fmt, ...)
            {
                ofstream out("output-file.txt");
                std::cout << "Cout to file";
                out << "Cout to file";
            }*/
            int main (int argc, char *argv[]) {
                FILE *outfile;
                char *mode = "a+";
                char outputFilename[] = "PRINT.log";
                outfile = fopen(outputFilename, mode);
                char bigfoot[] = "Hello 
    World!\n";
            int howbad = 10;
            printf(outfile, "\n--------\n");
            //myout();
            /* then i realized that i can't send the arguments to fn:PRINTs */
            printf(outfile, "%s %i",bigfoot, howbad); /* error here! I can't send bigfoot and howbad*/
            system("pause");
            return 0;
        }
    

    I have done it in COUT(caps, the commented part for the code above) . But I want to use normal std::cout, so how can I override it. And it should work for both sting and variables like

    int i = 5;
    cout << "Hello world" << i <<endl;
    

    Or are there anyway to capture stdout data, so that they can be easily written into file and console as well.

  • Filip Roséen - refp
    Filip Roséen - refp over 9 years
    -1; the answer by not-rightfold might not be the most detailed, but that's how you do it.
  • Rasmi Ranjan Nayak
    Rasmi Ranjan Nayak over 9 years
    You are right. I have a mix of printf and couts on the top of all these I do have third party library g-test and g-mock where I can not redirect all the couts to my user defined way. so how should I proceed?
  • MSalters
    MSalters over 9 years
    @RasmiRanjanNayak: If your third-party library uses the same C++ library, it shares the same std::cout object. In that case, not-rightfold's answer using std::cout.rd_buf will work because it changes the single std::cout object. If the library uses printf, you may have bigger problems.
  • MSalters
    MSalters over 9 years
    Bad idea. The formatting between the two streams is kept in two distinct stream objects, which can become out of sync. Easily, in fact, since the std::cout object is still accessible outside your calss. Furthermore, your stream isn't an ostream so it can't be passed to a function expecting one. Finally, I don't think you can use even std::endl on your stream.
  • stefan
    stefan over 9 years
    @MSalters I'm only providing a minimal working implementation. Surely one can inherit from ostream and your second point is gone. I hate using std::endl, but it's not that hard to copy that behaviour as well. Not modifying std::cout is the whole point of my answer..
  • Joe Beuckman
    Joe Beuckman about 9 years
    This is working great to trap std::cout so I can build an ncurses interface for a large existing program. Thank you!
  • HAL9000
    HAL9000 almost 3 years
    here the question is "how to do it", not "should I do it". You should have provided your useful (in some cases) advice after providing the answer to the specific question

Related