8

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.

Rasmi Ranjan Nayak
  • 11,510
  • 29
  • 82
  • 122

9 Answers9

14

If you have another stream buffer, you can just replace std::cout's:

std::cout.rdbuf(some_other_rdbuf);

See http://en.cppreference.com/w/cpp/io/basic_ios/rdbuf.

  • 1
    Indeed this simple. In this case, you just need a ""tee" streambuf which copies its output to two underlying streambufs. – MSalters Sep 24 '13 at 07:49
5

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());
    // ...
}
David G
  • 94,763
  • 41
  • 167
  • 253
  • 1
    This is working great to trap std::cout so I can build an ncurses interface for a large existing program. Thank you! – Joe Beuckman Mar 05 '14 at 03:02
2

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.

stefan
  • 10,215
  • 4
  • 49
  • 90
  • 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. – MSalters Sep 24 '13 at 14:47
  • @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.. – stefan Sep 24 '13 at 15:00
  • 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 – HAL9000 Jul 22 '20 at 13:29
1

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.

Community
  • 1
  • 1
Nicola Musatti
  • 17,834
  • 2
  • 46
  • 55
  • 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? – Rasmi Ranjan Nayak Sep 24 '13 at 07:54
  • @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 Sep 24 '13 at 14:34
0

If i guess correctly you want to log everything that goes to the output also into a file.

What you want is an observer pattern.

Replace all direct logging in your code with calls to a new relay. The logging relay sends your messages to the observers. One of your observers loggs the message to the screen. The other one loggs to the file. Avoid making your relay a singleton if possible.

This suggestion only works if you can edit all of your source files.

Johannes
  • 6,490
  • 10
  • 59
  • 108
0

std::cout writes to stdout file you can do the following on Linux and Windows

#include <stdio.h>
#include <iostream>



int main()
{
    freopen("test.txt", "w", stdout);
    std::cout << "Hello strange stdout\n";
}

to change it back use the following taken from here

#include <stdio.h>
#include <stdlib.h>

void main(void)
{
   FILE *stream ;
   if((stream = freopen("file.txt", "w", stdout)) == NULL)
      exit(-1);

   printf("this is stdout output\n");

   stream = freopen("CON", "w", stdout);

   printf("And now back to the console once again\n");
}

Note: The latter is windows only

A. H.
  • 942
  • 10
  • 20
-1

cout is normally implemented as an object instance so you can't override it in the way that you would overload / override a function or a class.

Your best bet is not to fight that - yes you could build a my_cout and #define cout my_cout but that would make your code obtuse.

For readability I'd leave cout as it is. It's a standard and everyone knows what it can and can't do.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
-1

Try using a macro - something like this (you'll need to add the includes) :

#define MY_COUT(theos,printThis) { cout << printThis ; theos <<  printThis; }

void test()
{
     ofstream myos;
    myos.open("testfile", ios::trunc|ios::out);
    int i = 7;
    MY_COUT(myos, "try this numbers" << i << i + 1 << endl);
    myos.close()
}
Avi Perel
  • 422
  • 3
  • 8
  • The correct way to do this is with an output operator or with a function (in case you need to use stream objects (not cout specifically) and with setting a custom output buffer in case you need cout specifically). There is no reason to use a macro here. – utnapistim Sep 24 '13 at 08:21
-1

There's already a Boost class for this: tee

MSalters
  • 173,980
  • 10
  • 155
  • 350