Custom C++ cout class - output to both console and log file
Asked Answered
P

7

9

I'm working on a program that makes heavy use of "cout << strSomething;" to log information to the console. I need to modify the program so that all console output goes to both the console AND a file. Although I can modify the "cout <<" in our code, there are several large third party libraries that also use "cout <<"; those libraries cannot be modified due to their licenses - so modifying all references to "cout <<" is not a solution. Also, the use of "wtee.exe" isn't possible due to the manner in which the command lines are executed.

I am using Visual Studio 2008. I've seen the posting at Google Groups: redirect cout to file, which appears to do EXACTLY what I want to do. The only problem is that the code won't compile. I get C2248 errors "cannot access protected member" on the ->overflow() and ->sync() method calls.

Would anyone know how to get this code to compile? Or an alternate way of redirecting cout to both console and file simultaneously?

Pridgen answered 9/5, 2011 at 20:39 Comment(3)
Duplicate, I think: https://mcmap.net/q/549315/-how-can-i-compose-output-streams-so-output-goes-multiple-places-at-onceLowery
(That contains both a Boost and non-Boost solution.)Lowery
Clever and simple solution here: https://mcmap.net/q/582927/-using-freopen-to-print-to-file-and-screenBasinet
P
1

The sync calls can be replaced with pubsync. As for the overflow call I think that may be a typo. as it looks as if it should be a call to sputc.

Patronizing answered 9/5, 2011 at 21:19 Comment(3)
Changing "sync" to "pubsync" compiles. But changing to "overflow" to "putc" fails. Error C2039 'putc' is not a member of std::basic_streambuf<_Elem,_Traits>Pridgen
Sorry, that was a typo. in my answer. I corrected it to sputc.Patronizing
In our situation (unable to use Boost, unable to modify 3rd party libraries), this was the best solution.Pridgen
G
12

The boost::iostreams::tee_device is made for this

#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/tee.hpp>

#include <fstream>
#include <iostream>

int
main()
{
    typedef boost::iostreams::tee_device<std::ostream, std::ofstream> Tee;
    typedef boost::iostreams::stream<Tee> TeeStream;

    std::ofstream file( "foo.out" );
    Tee tee( std::cout, file );

    TeeStream both( tee );

    both << "this goes to both std::cout and foo.out" << std::endl;

    return 0;
}

sample invocation:

samm$ ./a.out
this goes to both std::cout and foo.out
samm$ cat foo.out
this goes to both std::cout and foo.out
samm$ 
Giarla answered 9/5, 2011 at 21:9 Comment(2)
This works just fine for me - unfortunately, I've been asked to NOT include Boost as part of this application. Management, non-technical based decision. So - how do you do this in just std?Pridgen
@Jason it looks like there are some other answers that do not use boost. Good luck.Giarla
S
3

This easily extends to additional streams.

OstreamFork.hpp -- Distribute data to 2 streams simultaneously

#include <iomanip>
#include <fstream>
#include <iostream>
using namespace std ;

class ostreamFork           // Write same data to two ostreams
{
public:
  ostream& os1 ;
  ostream& os2 ;

  ostreamFork( ostream& os_one , ostream& os_two )
  : os1( os_one ) ,
    os2( os_two )
  {}

 } ;

                          // For data: int, long , ...
 template <class Data>
 ostreamFork& operator<<( ostreamFork& osf , Data d )
 {
   osf.os1 << d ; 
   osf.os2 << d ;
   return osf ;
 }
                        // For manipulators: endl, flush
 ostreamFork& operator<<( ostreamFork& osf , ostream& (*f)(ostream&)  )
 {
   osf.os1 << f ; 
   osf.os2 << f ;
   return osf ;
 }

                            // For setw() , ...
template<class ManipData>
 ostreamFork& operator<<( ostreamFork& osf , ostream& (*f)(ostream&, ManipData )  )
 {
   osf.os1 << f ; 
   osf.os2 << f ;
   return osf ;
 }

TestOstreamFork.cpp:

#include "stdafx.h"
#include <fstream>
  using namespace std ;
#include "ostreamFork.hpp"

int main(int argc, char* argv[])
{
  ofstream file( "test2.txt" ) ;
  ostreamFork osf( file , cout ) ;

  for ( int i = 0 ; i < 10 ; i++ )
  {
    osf << i << setw(10) << " " << 10*i << endl  ;
  }

    return 0 ;
}

Output to both cout and test2.txt:

0          0
1          10
2          20
3          30
4          40
5          50
6          60
7          70
8          80
9          90
Sully answered 27/8, 2018 at 2:7 Comment(0)
G
2

if you're desperate:

#define protected public
#include <iostream>
#undef protected

this is a gross hack, but it usually works.

Groenendael answered 9/5, 2011 at 20:47 Comment(1)
At this point, I'm desperate enough to take even an ugly hack. Unfortunately, with VS2008, it doesn't help. Same original errors.Pridgen
P
1

The sync calls can be replaced with pubsync. As for the overflow call I think that may be a typo. as it looks as if it should be a call to sputc.

Patronizing answered 9/5, 2011 at 21:19 Comment(3)
Changing "sync" to "pubsync" compiles. But changing to "overflow" to "putc" fails. Error C2039 'putc' is not a member of std::basic_streambuf<_Elem,_Traits>Pridgen
Sorry, that was a typo. in my answer. I corrected it to sputc.Patronizing
In our situation (unable to use Boost, unable to modify 3rd party libraries), this was the best solution.Pridgen
F
1

What you can do is capture the std::cout.rdbuf() with a pointer to std::streambuf, then i think you should be able to write all the outputs to std::cout to some file.

Farias answered 23/6, 2011 at 5:20 Comment(0)
F
0

you can just use a wrapper class to do so, somthing like this

#include <iostream>
#include <fstream>

...

class streamoutput
{
    std::ofstream fileoutput;
    public:
    streamoutput(char*filename){
        fileoutput.open(filename);
    }
    ~streamoutput(){
        fileoutput.close();
    }
    template<class TOut> streamoutput& operator <<(const TOut& data)
    {
        fileoutput << data;
        std::cout << data;
        return this;
    }
};

extern streamoutput cout("logfile.log");

declare cout like that and just change all your #include <iostream> to include this wrapper (remeber cout is external variable so you have to declere it in one of your source codes too).

Falsework answered 9/5, 2011 at 20:49 Comment(2)
This will work for the code that we write, were we can modify our #include <iostream> lines. But this won't work for the third-party code, that must also be using #include <iostream>, but we can't modify due to licensing.Pridgen
then you can just modify iostream file instead! no lisence can prevent you from that! but if you have a precompiled code that's totaly a diffrent story, but you can also write a wrapper program that runs yours and do all the console works for you. though it'll take somehow much more effort!Falsework
R
0

Sorry to warm this up so late, but this here should be a solution with redirection of cout to a teebuffer based on Dietmar Kühl's solution on Google groups.

Usage is simply

GetSetLog log("myfile.log");

During the lifetime of the object "log" everything will be written to both cout/cerr and file

https://sourceforge.net/p/getset/code/ci/master/tree/GetSet/GetSetLog.hxx

Reciprocity answered 14/10, 2016 at 13:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.