2009-02-10 12 views
6

Ich möchte dieses Schnipsel von Mr-Edd's iostreams article verwenden, um std :: Clog irgendwo zu drucken.Umleitung std :: cout zu einem benutzerdefinierten Schriftsteller

#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 einem mainloop, werde ich so etwas wie

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

tue Hier ist die ostringstream docs aber ich habe Probleme, den besten Weg zu verstehen, dies zu tun. Ich habe eine Methode, die den Text anzeigt, ich möchte es nur mit beliebigen Daten im ostringstream aufrufen.

Was ist der einfachste/beste Weg, um etwas an std :: clog gesendet zu einer Methode meiner Wahl weitergeleitet zu bekommen? ist es wie oben, und füllen Sie die while eof part (nicht sicher, wie), oder gibt es einen besseren Weg, sagen Sie, indem Sie irgendwo einen 'commit' Operator überladen, der meine Methode aufruft? Ich interessiere mich für schnell und einfach, ich möchte wirklich nicht beginnen, Spülen und so mit Boost-Iostreams zu definieren, wie der Artikel - das Zeug ist über meinen Kopf.

+0

Sie klarer sein könnte, was Ihre Frage ist? –

Antwort

11

Ich ermutige Sie, bei Boost.IOStreams zu suchen. Es scheint, Ihr Anwendungsfall gut zu passen, und es verwendet, ist überraschend einfach:

#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

Ihr Snippet funktioniert in einem leeren Projekt. Ich kopierte-pastete Ihre Senke in mein reales Projekt und kopierte Ihr Haupt irgendwo in eine init Methode, änderte NICHTS, und ich erhalte mehrere Seiten von erhöhten Unsinnfehlern. –

+0

(in dieser Zeile: bio :: stream_buffer sb;) –

+0

Ich würde gerne helfen, aber würde mehr Informationen über die Fehler, die Sie sehen. Die drakonische Größenbeschränkung für die Kommentare macht es hier nicht praktisch, daher schlage ich vor, dass Sie Ihren Code und die Fehler, die er generiert, in der Boost-Benutzerliste veröffentlichen. http://www.boost.org/community/groups.html#users –

7

I Denken Sie Sie möchten den Text aus dem Ostream, während es nicht leer ist. Sie könnten etwas tun wie folgt:

Lassen Sie mich wissen, wenn dies nicht das ist, was Sie wollten.

Natürlich ist die bessere Lösung, den mittleren Mann zu entfernen und einen neuen streambuf gehen, wohin Sie wirklich wollen es, keine Notwendigkeit, später zu prüfen. so etwas wie dieses (beachten Sie, dies macht es für jedes Zeichen, aber es gibt genügend Puffermöglichkeiten in streambufs auch):

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

Ich habe nicht erwartet, dass es so einfach ist, aber ich werde es versuchen. –

0

Wenn Sie nur den Inhalt des Ostringstream erhalten möchten, dann benutze sein str() Mitglied. Zum Beispiel:

string s = oss.str();  
4

Ich brauchte Ausgänge greifen auf std :: cout und std :: cerr von Bibliotheken von Drittanbietern und melden Sie sich mit ihnen log4cxx und Die ursprünglichen Ausgaben bleiben erhalten.

Das ist, was ich gefunden habe. Es ist ziemlich geradlinig:

  • Ich ersetze den alten Puffer eines ostream (wie std :: cout) mit meiner eigenen Klasse, damit ich Zugang zu bekommen, was jemals geschrieben steht.

  • Ich erstelle auch ein neues std :: ostream-Objekt mit dem alten Puffer, so dass ich weiterhin die Ausgabe an meine Konsole erhalten kann, außer es an meinen Logger zu senden. Was ich praktisch finde.

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

Dann, es zu benutzen:

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; 
+1

Könnte etwas Leistung herausholen, indem Sie die std :: string-Konstruktion innerhalb der isInfoEnabled-Prüfung verschieben. –

+0

Das funktioniert super! Ich habe nur intercept_stream geändert, um anstelle von char const * eine std :: string zu nehmen, da getLogger eine Zeichenkette verwendet und die string-Konstruktion innerhalb der isInfoEnabled-Kontrolle nach Rhys Ulerichs Vorschlag verschoben hat. – Fred

1

Für das log4cxx Beispiel Sie Überlauf außer Kraft setzen müssen() und sync() sonst die badbit immer nach dem ersten gesetzt Stream wird empfangen.

Siehe: 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; 
}