2009-08-10 16 views
9

Quelle est la bonne pratique pour générer une sortie verbeuse? actuellement, j'ai une fonctionQuelle est la bonne pratique pour générer une sortie verbeuse?

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

et chaque fois que je veux générer une sortie, je fais quelque chose comme

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

cependant, je ne pense pas que ce soit très élégant. donc je me demande ce qui serait un bon moyen de mettre en œuvre ce changement de verbosité?

+1

doesnt votre 'setVerbose' retour toujours 0? – Kredns

+0

oui, à moins que quelque chose d'extrêmement ésotérique se produise. c'est juste que j'ai un tas de fonctions setSomething() qui toutes renvoient 0 si l'opération a réussi et -1 sinon. donc c'est juste une question d'avoir une interface cohérente ... –

+0

Je ne comprends pas ce code.Est-ce un cas de montage mal tourné? –

Répondre

9

La manière la plus simple est de créer petite classe comme suit (ici est une version Unicode, mais vous pouvez facilement changer en version mono-octet):

#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); 
} 

fonction d'aide log a été modèle pour obtenir belle syntaxe d'appel. Ensuite, il pourrait être utilisé de la manière suivante:

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; 
} 

Vous pouvez modifier le niveau de verbosité lors de l'exécution en changeant la variable globale GLOBAL_LEVEL.

+0

J'aime cette réponse. J'ai vraiment seulement besoin de cette journalisation stdout très simple, pas de trucs de fichiers/réseau jamais nécessaire (j'espère). donc je vais essayer de garder le nombre de dépendances externes bas et aller pour ma propre implémentation. –

+0

Je trouve cette solution astucieuse et très utile, je souhaite pouvoir lui donner 10 upvotes! :-) –

+0

Je crois que la variable GLOBAL_LEVEL sera problématique à travers les unités de traduction, n'est-ce pas? Comment implémenteriez-vous cette variable? – quimnuss

3

Vous pouvez utiliser log4cpp

+0

Est-ce que cela a des capacités de journalisation asynchrones? – Alex

+0

Je ne sais pas si c'est le cas ou non. Pardon. – Glen

+3

Je suis d'accord que l'utilisation d'une bibliothèque de journalisation correcte est la réponse plutôt que de coder à la main quelque chose, bien que Log4cpp semble être moribond. http://logging.apache.org/log4cxx/index.html d'autre part est vivant et dynamique. Selon le site web, il supporte la journalisation asynchrone, bien que je ne pense pas que ce soit pertinent pour cette question. –

3

Vous pouvez envelopper votre fonctionnalité dans une classe qui prend en charge l'opérateur < < qui vous permet de faire quelque chose comme

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

Ensuite, vous pouvez faire quelque chose comme

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

J'aime l'idée d'encapsuler un système d'enregistrement des erreurs dans une classe. Vous pouvez stocker des choses comme l'emplacement du fichier de sortie et autres joyeusetés dans la classe, aussi. – Brian

+0

juste pour que je vous comprenne correctement: je voudrais créer une instance de la classe Trace appelée trace avant? et puis une autre question: pourquoi Trace :: Activer suffisamment pour définir l'état sur Activer? je suis encore assez nouveau en C++, et je suis un peu confus. peut-être que trois lignes de plus dans votre idée m'aideraient à mieux comprendre ce qui se passe. mais j'aime vraiment ton idée! –

+1

Oui, vous devez créer une instance de la classe Trace; cela pourrait être statiquement ou localement à chaque composant qui souhaitait l'utiliser. Le paramètre Trace :: Enable permet de basculer un indicateur interne dans la classe Trace (une instance de l'état enum). Cela agirait très bien comme std :: hex permet à std :: cout de sortir au format hexadécimal plutôt que décimal. Puisque vous allez écrire l'opérateur << vous pouvez supporter Trace :: Enable (ou tout autre spécificateur d'état tel que verbosité et autres) – 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

serait-il possible d'inclure 'std :: endl' dans la définition de log()? –

+0

Définir 'log (x)' peut être facilement remplacé par la fonction inline. Y at-il une raison d'utiliser définit ici? –

+0

En ce qui concerne std :: endl: aucun que je connaisse. Si vous voulez moins de code, vous pouvez toujours utiliser typedef, comme _e. Regardig # define/inline: aucune raison. –

2

1. Si vous utilisez g ++, vous pouvez utiliser l'option -D, cela permet au compilateur de définir une macro de votre choix.

Définition du

Par exemple:

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

2. Je suis d'accord ce n'est pas élégant non plus, donc pour le rendre un peu plus agréable:

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 vous pourrait utiliser comme printf:

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

3. Une troisième solution plus facile, et plus C++ et Unix like est de passer un argument à votre programme qui va être utilisé - comme la macro plus tôt - pour initialiser une variable particulière (qui pourrait être un const).

Exemple: ./myprogram $ -v

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