redirect std::cout to a custom writer

前端 未结 5 1185
生来不讨喜
生来不讨喜 2020-12-08 18:14

I want to use this snippet from Mr-Edd\'s iostreams article to print std::clog somewhere.

#include 
#include 
#include 

        
相关标签:
5条回答
  • 2020-12-08 18:17

    I think you want to pull the text from the ostream while it's not empty. You could do something like this:

    std::string s = oss.str();
    if(!s.empty()) {
        // output s here
        oss.str(""); // set oss to contain the empty string
    }
    

    Let me know if this isn't what you wanted.

    Of course, the better solution is to remove the middle man and have a new streambuf go wherever you really want it, no need to probe later. something like this (note, this does it for every char, but there is plenty of buffering options in streambufs as well):

    class outbuf : public std::streambuf {
    public:
        outbuf() {
            // no buffering, overflow on every char
            setp(0, 0);
        }
    
        virtual int_type overflow(int_type c = traits_type::eof()) {
            // add the char to wherever you want it, for example:
            // DebugConsole.setText(DebugControl.text() + c);
            return c;
        }
    };
    
    int main() {
        // set std::cout to use my custom streambuf
        outbuf ob;
        std::streambuf *sb = std::cout.rdbuf(&ob);
    
        // do some work here
    
        // make sure to restore the original so we don't get a crash on close!
        std::cout.rdbuf(sb);
        return 0;
    

    }

    0 讨论(0)
  • 2020-12-08 18:22

    I needed to grab outputs to std::cout and std::cerr from third party libraries and log them using log4cxx, and still retaining the original outputs.

    This is what I came up with. It's pretty straight-forward:

    • I replace the old buffer of an ostream (like std::cout) with my own class so that I get access to what ever is written to it.

    • I also create a new std::ostream object with the old buffer so that I can continue to get the output to my console, besides sending it to my logger. Which I find kind of handy.

    Code:

    class intercept_stream : public std::streambuf{
    public:
        intercept_stream(std::ostream& stream, char const* logger):
          _logger(log4cxx::Logger::getLogger(logger)),
          _orgstream(stream),
          _newstream(NULL)
        {
            //Swap the the old buffer in ostream with this buffer.
            _orgbuf=_orgstream.rdbuf(this);
            //Create a new ostream that we set the old buffer in
            boost::scoped_ptr<std::ostream> os(new std::ostream(_orgbuf));
            _newstream.swap(os);
        }
        ~intercept_stream(){
            _orgstream.rdbuf(_orgbuf);//Restore old buffer
        }
    protected:
        virtual streamsize xsputn(const char *msg, streamsize count){
            //Output to new stream with old buffer (to e.g. screen [std::cout])
            _newstream->write(msg, count);
            //Output to log4cxx logger
            std::string s(msg,count);
            if (_logger->isInfoEnabled()) {
                _logger->forcedLog(::log4cxx::Level::getInfo(), s, LOG4CXX_LOCATION); 
            }
            return count;
        }
    private:
        log4cxx::LoggerPtr _logger;
        std::streambuf*    _orgbuf;
        std::ostream&      _orgstream;
        boost::scoped_ptr<std::ostream>  _newstream;
    };
    

    Then to use it:

    std::cout << "This will just go to my console"<<std::endl;
    intercept_stream* intercepter = new intercept_stream(std::cout, "cout");
    std::cout << "This will end up in both console and my log4cxx logfile, yay!" << std::endl;
    
    0 讨论(0)
  • 2020-12-08 18:30

    I encourage you to look at Boost.IOStreams. It seems to fit your use-case nicely, and using it is surprisingly simple:

    #include <boost/iostreams/concepts.hpp> 
    #include <boost/iostreams/stream_buffer.hpp>
    #include <iostream>
    
    namespace bio = boost::iostreams;
    
    class MySink : public bio::sink
    {
    public:
        std::streamsize write(const char* s, std::streamsize n)
        {
            //Do whatever you want with s
            //...
            return n;
        }
    };
    
    int main()
    {
        bio::stream_buffer<MySink> sb;
        sb.open(MySink());
        std::streambuf * oldbuf = std::clog.rdbuf(&sb);
        std::clog << "hello, world" << std::endl;
        std::clog.rdbuf(oldbuf);
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-08 18:31

    For the log4cxx example you must override overflow() and sync() otherwise the badbit is always set after first stream is received.

    See: http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/fd9d973282e0a402/a872eaedb142debc

    InterceptStream::int_type InterceptStream::overflow(int_type c)
    {
        if(!traits_type::eq_int_type(c, traits_type::eof()))
        {
            char_type const t = traits_type::to_char_type(c);
            this->xsputn(&t, 1);
        }
        return !traits_type::eof();
    }
    
    int InterceptStream::sync()
    {
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-08 18:33

    If you just want to get the contents of the ostringstream, then use its str() member. For example:

    string s = oss.str();    
    
    0 讨论(0)
提交回复
热议问题