2010-02-01 12 views
14

Estoy tratando de poner en práctica mi propio flujo de depuración-salida QDebug() estilo, esto es básicamente lo que tengo hasta ahora:¿Cómo funciona QDebug() << cosas; agregar una nueva línea automáticamente?

struct debug 
{ 
#if defined(DEBUG) 
    template<typename T> 
    std::ostream& operator<<(T const& a) const 
    { 
     std::cout << a; 
     return std::cout; 
    } 
#else 
    template<typename T> 
    debug const& operator<<(T const&) const 
    { 
     return *this; 
    } 

    /* must handle manipulators (endl) separately: 
    * manipulators are functions that take a stream& as argument and return a 
    * stream& 
    */ 
    debug const& operator<<(std::ostream& (*manip)(std::ostream&)) const 
    { 
     // do nothing with the manipulator 
     return *this; 
    } 
#endif 
}; 

uso típico:

debug() << "stuff" << "more stuff" << std::endl; 

pero me gustaría no tener que agregar std :: endl;

Mi pregunta es, básicamente, ¿cómo puedo saber cuando el tipo de retorno del operador < < no va a ser utilizado por otro operador < < (y esto añadir endl)?

La única forma en que puedo pensar para lograr algo como esto sería crear una lista de cosas para imprimir con cada objeto temporal creado por debug(), luego imprimir todo, junto con la línea nueva final (y yo podría hacer cosas ingeniosas como insertar espacios) en ~ debug(), pero obviamente esto no es ideal ya que no tengo la garantía de que el objeto temporal vaya a ser destruido hasta el final del alcance (¿o no?).

+4

'qDebug()' solo crea objetos temporales. Sigue el símbolo y encontrarás algo como esto: 'QDebug qDebug() {return QDebug (QtDebugMsg); } ' –

Respuesta

8

Qt utiliza un método similar al @ Evan.Consulte a version of qdebug.h para conocer los detalles de la implementación, pero ellos transmiten todo a una secuencia de texto subyacente, y luego descargan la secuencia y una línea final sobre la destrucción del objeto QDebug temporal devuelto por qDebug().

+0

Enfriar gracias, eso es lo que me preguntaba, y confirma que el temporal se destruye rápidamente la mayor parte del tiempo :) – James

+1

@Autopulated: Olvidé la verborrea precisa, pero creo que el estándar exige que un objeto sin nombre se destruya inmediatamente después de la expresión que fue creado en. –

+2

Aha! lo encontré: 12.2/4 dice "Hay dos contextos en los que los temporales se destruyen en un punto diferente al final de la expresión completa". Lo que básicamente significa que, aparte de las dos excepciones que se incluirán en la lista, los objetos temporales se destruirán inmediatamente al final de la expresión. –

0

Se supone que la inserción de la secuencia (<<) y la extracción (>>) no son miembros.

Mi pregunta es, básicamente, cómo puedo decir cuando el tipo de retorno de operador < < no va a ser utilizado por otro operador < < (y esto añadir endl)?

No puede. Cree una función de miembro para anexarlo especialmente o anexar un endl una vez que las llamadas encadenadas hayan finalizado. Documente bien su clase para que los clientes sepan cómo usarla. Esa es tu mejor apuesta.

+0

Voy a ser el usuario principal de esto; Estoy intentando guardar escribiendo std :: endl una y otra vez. – James

+0

'endl' hace dos cosas: 1) agregar una nueva línea, que es algo que no puede controlar, ya que las cadenas pueden tener líneas nuevas incorporadas y 2) vacía la secuencia de salida, que puede controlar sin necesidad de ponerlas en el dtor. Su pregunta no era muy clara sobre lo que está tratando de lograr. – dirkgently

5

Cuando se escribe que este es el uso típico:

debug() << "stuff" << "more stuff" << std::endl; 

está definitivamente planeando construir un objeto de depuración cada vez que lo utilice? Si es así, usted debe ser capaz de obtener el comportamiento que desea haciendo que el destructor de depuración añadir la nueva línea:

~debug() 
{ 
    *this << std::endl; 

    ... the rest of your destructor ... 
} 

Eso no significa que no puede hacer algo como esto:

// this won't output "line1" and "line2" on separate lines 
debug d; 
d << "line1"; 
d << "line2"; 
+0

@last parte: funcionará si lo rodeas con llaves. Cuando el objeto de depuración sale del alcance, se llama a su destructor. – jmucchiello

+0

@jmucchiello: Se llamará al destructor al final de la instrucción, que es exactamente cuando desea que se inserte la nueva línea. – Ben

+0

@jmucchiello - lo que quiero decir con "will not work" es que line1 y line2 no estarán en líneas separadas como lo habrían sido si hubiera escrito 'debug() <<" line1 "; debug() << "line2()"; ' –

15

Algo como esto hacer:

struct debug { 
    debug() { 
    } 

    ~debug() { 
     std::cerr << m_SS.str() << std::endl; 
    } 

public: 
    // accepts just about anything 
    template<class T> 
    debug &operator<<(const T &x) { 
     m_SS << x; 
     return *this; 
    } 
private: 
    std::ostringstream m_SS; 
}; 

cual debería permitirle hacer cosas como esta:

debug() << "hello world"; 

He utilizado un patrón como este combinado con un bloqueo para proporcionar un sistema de registro como el flujo que puede garantizar que las entradas de registro se escriban atómicamente.

NOTA: código no probado, pero debería funcionar :-)

+0

Tiene un error: el destructor debería llamarse "depuración". Además, ¿por qué utilizar el ostringstream intermedio? ¿No puedes simplemente enviar cosas a cerr cuando llegan a tu operador < Manuel

+3

He arreglado el nombre del destructor (copiar/pegar el error). pero el hilo de cadena tiene un propósito. Lo hace para que la salida no se escriba hasta que se llame al destructor (lo que permite evitar problemas de enhebrado). –

Cuestiones relacionadas