2009-02-10 10 views
6

Voglio utilizzare questo snippet da Mr-Edd's iostreams article per stampare std :: clog da qualche parte.reindirizza std :: cout a un writer personalizzato

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

così, in un ciclo principale, farò qualcosa di simile

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

Ecco il ostringstream docs ma sto avendo difficoltà a capire il modo migliore per farlo. Ho un metodo che visualizza il testo, voglio solo chiamarlo con tutti i dati nel stringstream.

Qual è il modo più semplice/migliore per inviare qualcosa a std: clog reindirizzato a un metodo di mia scelta? è come sopra, e riempi la parte! eof (non so come), o c'è un modo migliore, ad esempio sovraccaricando qualche operatore 'commit' da qualche parte che chiama il mio metodo? Sto cercando di farlo in modo facile e veloce, non voglio davvero iniziare a definire i lavandini e così via con gli iostreams di boost come fa l'articolo - quella roba mi è passata per la testa.

+0

si potrebbe essere più chiari su ciò che la tua domanda è? –

risposta

11

vi incoraggio a guardare Boost.IOStreams. Sembra per misura il vostro caso d'uso piacevolmente, ed il suo utilizzo è sorprendentemente semplice:

#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

lo snippet funziona in un progetto vuoto. Ho copiato il tuo sink nel mio vero progetto e ho copiato il tuo main in un metodo init da qualche parte, ho cambiato NOTHING e ho ricevuto diverse pagine di errori nonsense. –

+0

(su questa riga: bio :: stream_buffer sb;) –

+0

Mi piacerebbe aiutare, ma avrei bisogno di maggiori informazioni sugli errori che vedi. Il limite di dimensioni draconiane dei commenti non lo rende pratico qui, quindi ti suggerisco di pubblicare il tuo codice e gli errori che genera nella lista degli utenti Boost. http://www.boost.org/community/groups.html#users –

7

I pensare si desidera estrarre il testo dall'ostream mentre non è vuoto. Potresti fare qualcosa del genere:

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

Fammi sapere se questo non è quello che volevi.

Ovviamente, la soluzione migliore è quella di rimuovere l'uomo medio e avere un nuovo streambuf andare ovunque si davvero lo desidera, non c'è bisogno di sondare più tardi. qualcosa di simile (nota, questo lo fa per ogni carattere, ma c'è un sacco di opzioni di tamponamento in streambuf pure):

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

non mi aspettavo che fosse così semplice, ma ci provo. –

0

Se si desidera ottenere il contenuto del ostringstream, quindi usa il suo membro str(). Per esempio:

string s = oss.str();  
4

avevo bisogno di prendere le uscite a std :: cout e std :: cerr da librerie di terze parti e li registro utilizzando log4cxx, e mantenendo ancora le uscite originali.

Questo è quello che mi è venuto in mente. E 'piuttosto straight-forward:

  • ho sostituire il vecchio tampone di un ostream (come std :: cout) con la mia classe in modo che ottengo l'accesso a ciò che mai è scritto ad esso.

  • Inoltre creo un nuovo oggetto std :: ostream con il vecchio buffer in modo che possa continuare a ricevere l'output sulla mia console, oltre a inviarlo al mio logger. Che trovo a portata di mano.

Codice:

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

Poi, per usarlo:

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

È possibile modificare alcune prestazioni spostando il contruction std :: string all'interno del controllo isInfoEnabled. –

+0

Funziona alla grande! Ho solo modificato intercept_stream per prendere uno std :: string invece di char const * dato che getLogger prende una stringa e ha spostato la costruzione della stringa all'interno del controllo isInfoEnabled per suggerimento di Rhys Ulerich. – Fred

1

Per l'esempio log4cxx è necessario eseguire l'override di overflow() e sync() altrimenti la badbit è sempre impostato dopo la prima flusso è ricevuto.

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