Mirror console output to file in c++

2019-02-23 01:37发布

问题:

In C++, is there a smart way to mirror output from stdout to both the console and the file? I'm hoping there is a way to do it like in this question.

Edit: It would be nice to be able to do this with just the standard libraries (ie: no boost)..

回答1:

Alternatively, just start your program so it's piped to the tee command.



回答2:

You could try a Tee Device provided by Boost.Iostreams.

A Tee device directs output to multiple streams. As far as I know, you can chain them to reach theoretically infinite output devices from one write call.

This answer shows an example for how to do exactly what you want.



回答3:

You can do this by creating a class that extends std::streambuf, and has a std::ofstream member. After overriding the std::streambuf::overflow and std::streambuf::sync member functions, you'll be all set.

Most of the code below comes from here. The stuff I've added ("ADDED:") for file-mirroring is pointed out. This might be overly complex as I'm at work and can't pore over it fully to simplify it, but it works - the bonus of doing it this way (instead of just using a std::streambuf* is that any code (say you have an external library) that writes to std::cout will write to your file.

mystreambuf.h

#ifndef MYSTREAMBUF_H
#define MYSTREAMBUF_H

template <typename charT, typename traits = std::char_traits<charT> >
class mystreambuf : public std::basic_streambuf<charT, traits>
{
public:
    // The size of the input and output buffers.
    static const size_t BUFF_SIZE = 1024;
    typedef traits traits_type;
    typedef typename traits_type::int_type int_type;
    typedef typename traits_type::pos_type pos_type;
    typedef typename traits_type::off_type off_type;

    // You can use any method that you want, but here, we'll just take in a raw streambuf as a
    // slave I/O object.  xor_char is what each character is xored with before output.
    explicit mystreambuf(std::streambuf* buf)
        : out_buf_(new charT[BUFF_SIZE])
    {
        // ADDED: store the original cout stream and open our output file
        this->original_cout = buf;
        outfile.open("test.txt");

        // Initialize the put pointer.  Overflow won't get called until this buffer is filled up,
        // so we need to use valid pointers.
        this->setp(out_buf_, out_buf_ + BUFF_SIZE - 1);
    }

    // It's a good idea to release any resources when done using them.
    ~mystreambuf() {
        delete [] out_buf_;
        // ADDED: restore cout, close file
        std::cout.rdbuf(original_cout);
        outfile.flush();
        outfile.close();
    }

protected:
    // This is called when there are too many characters in the buffer (thus, a write needs to be performed).
    virtual int_type overflow(int_type c);
    // This is called when the buffer needs to be flushed.
    virtual int_type sync();


private:
    // Output buffer
    charT* out_buf_;
    // ADDED: tracking the original std::cout stream & the file stream to open
    std::streambuf* original_cout;
    std::ofstream outfile;
};

#endif

mystreambuf.cpp

// Based on class by perfectly.insane from http://www.dreamincode.net/code/snippet2499.htm

#include <fstream>
#include <iostream>
#include <streambuf>

#include "mystreambuf.h"

// This function is called when the output buffer is filled.
// In this function, the buffer should be written to wherever it should
// be written to (in this case, the streambuf object that this is controlling).
template <typename charT, typename traits>
typename mystreambuf<charT, traits>::int_type
mystreambuf<charT, traits>::overflow(typename mystreambuf<charT, traits>::int_type c)
{
    charT* ibegin = this->out_buf_;
    charT* iend = this->pptr();

    // Reset the put pointers to indicate that the buffer is free
    // (at least it will be at the end of this function).
    setp(out_buf_, out_buf_ + BUFF_SIZE + 1);

    // If this is the end, add an eof character to the buffer.
    // This is why the pointers passed to setp are off by 1
    // (to reserve room for this).
    if(!traits_type::eq_int_type(c, traits_type::eof())) {
        *iend++ = traits_type::to_char_type(c);
    }

    // Compute the write length.
    int_type ilen = iend - ibegin;

    // ADDED: restore cout to its original stream, output to it, output to the file, then set cout's stream back to this, our streambuf)
    std::cout.rdbuf(original_cout);
    out_buf_[ilen] = '\0';
    std::cout << out_buf_;
    outfile << out_buf_;
    std::cout.rdbuf(this);

    return traits_type::not_eof(c);
}

// This is called to flush the buffer.
// This is called when we're done with the file stream (or when .flush() is called).
template <typename charT, typename traits>
typename mystreambuf<charT, traits>::int_type
mystreambuf<charT, traits>::sync()
{
    return traits_type::eq_int_type(this->overflow(traits_type::eof()),
                                    traits_type::eof()) ? -1 : 0;
}


int main(int argc, char* argv[]) {
    mystreambuf<char> filter(std::cout.rdbuf());
    std::cout.rdbuf( &filter );
    std::cout << "Hello World" << std::endl;

    return 0;
}

hope this helps; cheers



标签: c++ io stdout