views:

1544

answers:

4

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

#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>

int main()
{
    std::ostringstream oss;

    // Make clog use the buffer from oss
    std::streambuf *former_buff =
        std::clog.rdbuf(oss.rdbuf());

    std::clog << "This will appear in oss!" << std::flush;

    std::cout << oss.str() << '\\n';

    // Give clog back its previous buffer
    std::clog.rdbuf(former_buff);

    return 0;
}

so, in a mainloop, I will do something like

while (! oss.eof())
{
    //add to window text somewhere
}

Here's the ostringstream docs but I'm having trouble understanding the best way to do this. I have a method that displays the text, I just want to call it with any data in the ostringstream.

What is the easiest/best way to get anything sent to std::clog redirected to a method of my choice? is it as above, and fill in the while !eof part (not sure how), or is there a better way, say by overloading some 'commit' operator somewhere that calls my method? I'm loking for quick and easy, I really don't want to start defining sinks and such with boost iostreams as the article does - that stuff is way over my head.

+4  A: 

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;

}

Evan Teran
i didn't expect it to be anywhere near that simple, but i'll give it a try.
Dustin Getz
A: 

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

string s = oss.str();
anon
+5  A: 

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>

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::clog.rdbuf(&sb);
    std::clog << "hello, world" << std::endl;

    return 0;
}
Éric Malenfant
your snippet works in a blank project. i copy-pasted your sink into my real project and copied your main into an init method somewhere, changed NOTHING, and I get several pages of boost nonsense errors.
Dustin Getz
(on this line: bio::stream_buffer<MySink> sb;)
Dustin Getz
I'd love to help, but would need more information about the errors you see. The draconian size limit on the comments does not make it practical here, so I suggest you post your code and the errors it generates on the Boost users list. http://www.boost.org/community/groups.html#users
Éric Malenfant
+2  A: 

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;
mandrake