2009-08-10 33 views
9

¿cuál es una buena práctica para generar resultados detallados? Actualmente, tengo una función¿Cuál es una buena práctica para generar resultados detallados?

bool verbose; 
int setVerbose(bool v) 
{ 
    errormsg = ""; 
    verbose = v; 
    if (verbose == v) 
     return 0; 
    else 
     return -1; 
} 

y siempre que lo desee para generar la salida, hago algo así como

if (debug) 
    std::cout << "deleting interp" << std::endl; 
embargo

, no creo que eso es muy elegante. entonces me pregunto ¿cuál sería una buena forma de implementar este cambio de verbosidad?

+1

Dosis no siempre su 'setVerbose' devolver 0? – Kredns

+0

sí, a menos que ocurra algo extremadamente esotérico. es solo que tengo un conjunto de funciones setSomething() que devuelven 0 si la operación fue exitosa y -1 si no. así que solo se trata de tener una interfaz coherente ... –

+0

No entiendo este código.¿Es este un caso de edición que salió mal? –

Respuesta

9

La manera más simple es la creación de pequeña clase de la siguiente manera (en este caso es la versión Unicode, pero se puede cambiar fácilmente a la versión de un solo byte):

#include <sstream> 
#include <boost/format.hpp> 
#include <iostream> 
using namespace std; 

enum log_level_t { 
    LOG_NOTHING, 
    LOG_CRITICAL, 
    LOG_ERROR, 
    LOG_WARNING, 
    LOG_INFO, 
    LOG_DEBUG 
}; 

namespace log_impl { 
class formatted_log_t { 
public: 
    formatted_log_t(log_level_t level, const wchar_t* msg) : fmt(msg), level(level) {} 
    ~formatted_log_t() { 
     // GLOBAL_LEVEL is a global variable and could be changed at runtime 
     // Any customization could be here 
     if (level <= GLOBAL_LEVEL) wcout << level << L" " << fmt << endl; 
    }   
    template <typename T> 
    formatted_log_t& operator %(T value) { 
     fmt % value; 
     return *this; 
    }  
protected: 
    log_level_t  level; 
    boost::wformat  fmt; 
}; 
}//namespace log_impl 
// Helper function. Class formatted_log_t will not be used directly. 
template <log_level_t level> 
log_impl::formatted_log_t log(const wchar_t* msg) { 
    return log_impl::formatted_log_t(level, msg); 
} 

Función de ayuda log se hizo plantilla para obtener sintaxis de llamada agradable. Entonces podría ser utilizado de la siguiente manera:

int main() 
{ 
    // Log level is clearly separated from the log message 
    log<LOG_DEBUG>(L"TEST %3% %2% %1%") % 5 % 10 % L"privet"; 
    return 0; 
} 

Usted podría cambiar el nivel de detalle en tiempo de ejecución mediante el cambio de variable global GLOBAL_LEVEL.

+0

me gusta esta respuesta. realmente solo necesito este muy simple registro de stdout, sin necesidad de archivos/redes (espero). así que trataré de mantener baja la cantidad de dependencias externas e iré por mi propia implementación. –

+0

Encuentro esta solución inteligente y muy útil, ¡me gustaría poder darle 10 votos favorables! :-) –

+0

Creo que la variable GLOBAL_LEVEL será problemática en las unidades de traducción, ¿verdad? ¿Cómo implementarías esta variable? – quimnuss

3

Usted podría utilizar log4cpp

+0

¿Tiene eso capacidades de registro asíncronas? – Alex

+0

No sé si lo hace o no. lo siento. – Glen

+3

Acepto que usar una biblioteca de registro adecuada es la respuesta en lugar de codificar manualmente algo, aunque parece que Log4cpp está moribundo. http://logging.apache.org/log4cxx/index.html por otro lado está vivo y coleando. Según el sitio web, tiene soporte para el registro asincrónico, aunque no creo que sea relevante para esta pregunta. –

3

Usted puede envolver su funcionalidad en una clase que soporta el operador < < que le permite hacer algo como

class Trace { 
    public: 
     enum { Enable, Disable } state; 
    // ... 
    operator<<(...) 
}; 

A continuación, puede hacer algo como

trace << Trace::Enable; 
trace << "deleting interp" 
+0

Me gusta la idea de incluir un sistema de registro de errores en una clase. También puede almacenar cosas como la ubicación del archivo de salida y otras cosas en la clase. – Brian

+0

solo para que lo entienda correctamente: ¿crearía una instancia de la clase Trace llamada trace antes? y luego otra pregunta: ¿por qué Trace :: habilita lo suficiente para establecer el estado en Habilitar? Todavía soy bastante nuevo en C++, y estoy un poco confundido. quizás unas tres líneas más en tu idea me ayudarían a entender mejor lo que está pasando. pero definitivamente me gusta tu idea! –

+1

Sí, necesitaría crear una instancia de la clase Trace; esto podría ser de manera estática o local para cada componente que deseara usarlo. Trace :: Enable alternaría un indicador interno dentro de la clase Trace (una instancia del estado enum). Esto actuaría de manera muy parecida a std :: hex permite que std :: cout salga en formato hexadecimal en lugar de decimal. Como va a escribir el operador << puede admitir Trace :: Enable (u otros especificadores de estado como la verbosidad y demás) – ezpz

8
enum LogLevel 
{ 
    INF = 0, 
    WAR = 1, 
    ERR = 2 
}; 

LogLevel threshold = WAR; 

class mystreambuf: public std::streambuf {}; 
mystreambuf nostreambuf; 
std::ostream nocout(&nostreambuf); 
#define log(x) ((x >= threshold)? std::cout : nocout) 

int main() 
{ 
    log(INF) << "No hello?" << std::endl;  // Not printed on console, too low log level. 
    log(ERR) << "Hello world!" << std::endl; // Will print. 
    return 0; 
} 
+0

¿existiría la posibilidad de 'incluir' std :: endl en la definición de log()? –

+0

Definir 'log (x)' se puede reemplazar fácilmente con la función en línea. ¿Hay alguna razón para usar define aquí? –

+0

En cuanto a std :: endl: ninguna que yo sepa. Si quiere menos código, siempre puede usar typedef, como _e. Regardig # define/inline: sin motivo. –

2

1. Si usa g ++ puede usar el distintivo -D, esto le permite al compilador definir una macro de su elección.

Definición de la

Por ejemplo:

#ifdef DEBUG_FLAG 
printf("My error message"); 
#endif 

2. Estoy de acuerdo que esto no es elegante, ya sea, por lo que para que sea un poco más agradable:

void verbose(const char * fmt, ...) 
{ 
va_list args; /* Used as a pointer to the next variable argument. */ 
va_start(args, fmt); /* Initialize the pointer to arguments. */ 

#ifdef DEBUG_FLAG 
printf(fmt, &args); 
#endif 
/*This isn't tested, the point is to be able to pass args to 
printf*/ 
} 

Que podría utilizar como printf:

verbose("Error number %d\n",errorno); 

3. Una tercera solución más fácil, y más C++ y Unix como es pasar un argumento a su programa que se va a usar -como la macro anterior- para inicializar una variable particular (que podría ser un const global)

Ejemplo: $ ./myprogram -v

if(optarg('v')) static const verbose = 1; 
Cuestiones relacionadas