c++ - food - Implementando un std:: ostream no operativo
food hashtags 2018 (5)
Estoy buscando hacer una clase de registro que tenga miembros como Info, Error, etc. que puedan enviarse de forma configurable a consola, archivo o en ninguna parte.
Para mayor eficiencia, me gustaría evitar la sobrecarga de formatear los mensajes que se van a descartar (es decir, mensajes de información cuando no se está ejecutando en un modo detallado). Si implemento un std :: streambuf personalizado que se envía a ninguna parte, me imagino que la capa std :: ostream seguirá haciendo todo el formateo. ¿Alguien puede sugerir una manera de tener un std :: ostream verdaderamente "nulo" que evite hacer ningún tipo de trabajo en los parámetros que se le pasan con <<
?
Gracias.
¿Por qué no utilizar soluciones de registro existentes utilizadas por millones de usuarios? log4j, log4net, log4cxx .., por nombrar solo algunos ...
Para evitar que las invocaciones de operator<<()
realicen el formateo, debe conocer el tipo de secuencia en tiempo de compilación. Esto se puede hacer con macros o con plantillas.
Mi plantilla de solución sigue.
class NullStream {
public:
void setFile() { /* no-op */ }
template<typename TPrintable>
NullStream& operator<<(TPrintable const&)
{ /* no-op */ }
}
template<class TErrorStream> // add TInfoStream etc
class Logger {
public:
TErrorStream& errorStream() {
return m_errorStream;
}
private:
TErrorStream m_errorStream;
};
//usage
int main() {
Logger<std::ofstream> normal_logger; // does real output
normal_logger.errorStream().open("out.txt");
normal_logger.errorStream() << "My age is " << 19;
Logger<NullStream> null_logger; // does zero output with zero overhead
null_logger.errorStream().open("out.txt"); // no-op
null_logger.errorStream() << "My age is " << 19; // no-op
}
Dado que tiene que hacer esto en tiempo de compilación, por supuesto es bastante inflexible.
Por ejemplo, no puede decidir el nivel de registro en tiempo de ejecución desde un archivo de configuración.
Probablemente necesite algo más que el formato de texto y el filtrado de mensajes. ¿Qué hay de multihilo?
Implementaría la sincronización de filtrado y multihilo como responsabilidad de una clase separada.
Sin embargo, el registro no es un problema tan simple, y trataría de utilizar las soluciones de registro existentes, en lugar de desarrollar una nueva.
Un rápido Google apareció con este ejemplo que puede ser de utilidad. No ofrezco ninguna garantía, excepto que compila y ejecuta :-)
#include <streambuf>
#include <ostream>
template <class cT, class traits = std::char_traits<cT> >
class basic_nullbuf: public std::basic_streambuf<cT, traits> {
typename traits::int_type overflow(typename traits::int_type c)
{
return traits::not_eof(c); // indicate success
}
};
template <class cT, class traits = std::char_traits<cT> >
class basic_onullstream: public std::basic_ostream<cT, traits> {
public:
basic_onullstream():
std::basic_ios<cT, traits>(&m_sbuf),
std::basic_ostream<cT, traits>(&m_sbuf)
{
init(&m_sbuf);
}
private:
basic_nullbuf<cT, traits> m_sbuf;
};
typedef basic_onullstream<char> onullstream;
typedef basic_onullstream<wchar_t> wonullstream;
int main() {
onullstream os;
os << 666;
}
todo, gracias por compartir el código, solo hago una prueba, luego el método de Neil seguirá formateando las cuerdas, por ejemplo:
#include <streambuf>
#include <ostream>
#include <iostream>
using namespace std;
template <class cT, class traits = std::char_traits<cT> >
class basic_nullbuf: public std::basic_streambuf<cT, traits> {
typename traits::int_type overflow(typename traits::int_type c)
{
return traits::not_eof(c); // indicate success
}
};
template <class cT, class traits = std::char_traits<cT> >
class basic_onullstream: public std::basic_ostream<cT, traits> {
public:
basic_onullstream():
std::basic_ios<cT, traits>(&m_sbuf),
std::basic_ostream<cT, traits>(&m_sbuf)
{
init(&m_sbuf);
}
private:
basic_nullbuf<cT, traits> m_sbuf;
};
typedef basic_onullstream<char> onullstream;
typedef basic_onullstream<wchar_t> wonullstream;
class MyClass
{
int a;
friend ostream& operator<< (ostream&, MyClass const&);
};
ostream& operator<<(ostream& out,MyClass const& b)
{
std::cout<<"call format function!!";
out << b.a;
return out;
}
int main() {
onullstream os;
MyClass obj;
os<<obj;
}
Al ejecutar este programa, encontrará que se llamará "ostream & operator << (ostream & out, MyClass const & b)". Por lo tanto, aún se llamará al formato obj. Por lo tanto, todavía no podemos evitar la sobrecarga de formatear los mensajes.