c++ - redireccionar std:: cout a un escritor personalizado
stream stringstream (5)
Quiero utilizar este fragmento del artículo iostreams de Mr-Edd para imprimir std :: clog en algún lugar.
#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;
}
entonces, en un mainloop, haré algo como
while (! oss.eof())
{
//add to window text somewhere
}
Aquí están los documentos ostringstream, pero estoy teniendo problemas para entender la mejor manera de hacerlo. Tengo un método que muestra el texto, solo quiero llamarlo con cualquier dato en ostringstream.
¿Cuál es la forma más fácil / mejor para redirigir todo lo enviado a std :: clog a un método de mi elección? ¿es como arriba, y completa el tiempo de la parte (no estoy seguro de cómo), o hay una mejor manera, por ejemplo, sobrecargando algún operador ''commit'' en alguna parte que llame a mi método? Estoy buscando rápido y fácil, realmente no quiero empezar a definir sumideros y cosas así, como lo hace el artículo, esas cosas están por encima de mi cabeza.
Si solo desea obtener el contenido de ostringstream, utilice su miembro str (). Por ejemplo:
string s = oss.str();
Creo que quieres sacar el texto del ostream mientras no está vacío. Podrías hacer algo como esto:
std::string s = oss.str();
if(!s.empty()) {
// output s here
oss.str(""); // set oss to contain the empty string
}
Avísame si esto no es lo que querías.
Por supuesto, la mejor solución es eliminar al intermediario y hacer que un streambuf nuevo vaya donde realmente lo desee, sin necesidad de sondear más tarde. algo como esto (nota, esto lo hace por cada char, pero también hay un montón de opciones de búfer en streambufs):
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;
}
Necesitaba obtener salidas para std :: cout y std :: cerr de bibliotecas de terceros y registrarlas usando log4cxx, y aún conservando los resultados originales.
Esto es lo que se me ocurrió. Es bastante directo:
Sustituyo el viejo buffer de un ostream (como std :: cout) con mi propia clase para que tenga acceso a lo que esté escrito en él.
También creo un nuevo objeto std :: ostream con el búfer antiguo para poder continuar obteniendo el resultado en mi consola, además de enviarlo a mi registrador. Lo cual me parece útil.
Código:
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;
};
Entonces para 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;
Para el ejemplo de log4cxx, debe anular overflow () y sync (), de lo contrario, el badbit siempre se establece después de recibir la primera transmisión.
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;
}
Te animo a mirar Boost.IOStreams
. Parece ajustarse muy bien a tu caso de uso, y usarlo es sorprendentemente 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;
}