2009-04-17 9 views
30

Estoy buscando hacer una clase de registro que tenga miembros como Info, Error, etc., que puedan configurarse de manera configurable para consola, archivo o en ninguna parte.Implementando un no-op std :: ostream

Para mayor eficiencia, me gustaría evitar la sobrecarga de formatear 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 pasen con <<?

Gracias.

+0

no me preocupe. solo use una secuencia nula como se muestra en neil. la clase no necesita un mejor rendimiento, porque obviamente si no tiene un objetivo nulo, el formateo * tiene * que hacerse, por lo que obviamente no es crítico. solo mis 2 centavos –

+0

hmm, pero parece que está pensado como una cosa de "salida de depuración"? De una manera que he visto es así: out() << a << b ...; y out() devuelve struct f {}; sin la plantilla f const & operator << (f const & f_, T const) {return f_; }, y luego distinguir las diferentes estructuras de retorno según el nivel de registro. o hacer diferentes funciones de salida o lo que sea. –

Respuesta

4

Para evitar que las invocaciones 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 solución de plantilla 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.

+0

+1: simple, limpio, hace bien el trabajo. Tenga en cuenta que, por ejemplo,"NullStream s; s << expensive_function();" lo más probable es que todavía evalúe la función costosa(), especialmente si vive en otro módulo. –

+3

También mencionaré que, a diferencia de Onullstream de Neil, su NullStream no se puede pasar a una función que espera un argumento ostream u ostream *. –

+0

expensive_function() _definitely_ se evaluará sin importar dónde viva. No hay forma de evitar esas macros de restricción y compilación condicional :) ... en cuanto a Onullstream de Neil, no creo que cumpla con el requisito de "cero sobrecarga de formato" :) –

0

Probablemente necesite algo más que el formato de texto y el filtrado de mensajes. ¿Qué hay de multihilo?

Implementaré 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.

15

Un rápido Google se le ocurrió con este ejemplo que puede ser de utilidad. Ofrezco no hay garantías, salvo que se 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; 
} 
+0

+1. Sí, supongo que derivar de std :: basic_ostream <> es necesario si quieres pasar un flujo de nada en una función esperando un parámetro ostream & o ostream * - el truco de Iraimbilanja no funcionará allí. –

+0

solo se puede aceptar como 'ostream *', no 'ostream &', corrrect? – athos

0

Por qué no usar soluciones de registro existentes utilizados por millones de usuarios? log4j, log4net, log4cxx .., por nombrar sólo unos pocos ..

13

todo, gracias por compartir el código, acabo de hacer una prueba, entonces el método de Neil todavía hará el formateo de cadena, 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; 
} 

la ejecución de este programa, se encuentra que "ostream & operador < < (ostream & cabo, MiClase const & b)" serán llamados. Por lo tanto, aún se llamará al formato obj. Por lo tanto, todavía no podemos evitar la sobrecarga de formatear los mensajes.

+0

AFAIK el optimizador puede eliminar el código que se considera que no tiene efectos secundarios. ¡Como su llamada a 'std :: cout <<" function format function !! "' tiene efectos secundarios, entonces el optimizador no eliminará esto. Sin embargo, sin la llamada, es posible que esto se elimine –

Cuestiones relacionadas