2009-08-10 17 views
9

jaka jest dobra praktyka do generowania szczegółowych wyników? Obecnie mam funkcjęCo to jest dobra praktyka do generowania szczegółowych wyników?

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

i kiedy chcę, aby wygenerować dane wyjściowe, zrobić coś podobnego

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

Jednak nie sądzę, że to bardzo elegancki. więc zastanawiam się, jaki byłby dobry sposób na wprowadzenie tego przełącznika gadatliwości?

+1

nie robi swój 'setVerbose' zawsze zwraca 0? – Kredns

+0

tak, chyba że wydarzy się coś niesamowicie ezoterycznego. po prostu mam kilka funkcji setSomething(), które zwracają 0, jeśli operacja się powiodła, a -1, jeśli nie. więc jest to kwestia posiadania spójnego interfejsu ... –

+0

Nie rozumiem tego kodu.Czy to przypadek edytowania nie jest nie tak? –

Odpowiedz

9

Najprostszy sposób jest stworzenie małej klasy jak następuje (tu jest wersja Unicode, ale łatwo można go zmienić do wersji single-bajtowy):

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

Helper funkcja log powstał szablon, aby uzyskać ładny składnię wywołania. Może to być używane w następujący sposób:

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

Można zmienić poziom oznajmiania w czasie wykonywania przez zmianę globalną GLOBAL_LEVEL zmienną.

+0

Podoba mi się ta odpowiedź. Naprawdę potrzebuję tylko tego bardzo prostego logowania do standardu, bez żadnych plików/sieci kiedykolwiek potrzebnych (mam nadzieję). więc postaram się utrzymać niską liczbę zewnętrznych zależności i pójdę na własną implementację. –

+0

Uważam, że to rozwiązanie jest sprytne i bardzo pomocne, chciałbym dać mu 10 przegranych! :-) –

+0

Wierzę, że zmienna GLOBAL_LEVEL będzie problematyczna dla jednostek tłumaczeniowych, czy mam rację? Jak zaimplementować tę zmienną? – quimnuss

3

Można użyć log4cpp

+0

Czy ma to asynchroniczne możliwości rejestrowania? – Alex

+0

Nie wiem, czy to prawda, czy nie. Przepraszam. – Glen

+3

Zgadzam się, że używanie odpowiedniej biblioteki do rejestrowania jest odpowiedzią, a nie ręcznym kodowaniem, chociaż Log4cpp wydaje się być agresywny. Z drugiej strony http://logging.apache.org/log4cxx/index.html żyje i kopie. Według strony internetowej ma wsparcie dla rejestrowania asynchronicznego, ale nie sądzę, że jest to istotne dla tego pytania. –

3

Można owinąć funkcjonalność w klasie, który obsługuje operator < < który pozwala zrobić coś jak

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

Następnie można zrobić coś takiego

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

Podoba mi się pomysł zawinięcia systemu rejestracji błędów w klasie. Możesz przechowywać rzeczy takie jak lokalizacja pliku wyjściowego i inne elementy w klasie. – Brian

+0

po to, aby zrozumieć cię poprawnie: Czy utworzyć instancję klasy Trace o nazwie trace? a następnie kolejne pytanie: dlaczego Trace :: Włącz wystarczająco, aby ustawić stan na Enable? wciąż jestem dość nowy w C++ i jestem nieco zdezorientowany. być może jakieś trzy linijki więcej w twoim pomyśle pomogłyby mi lepiej zrozumieć, co się dzieje. ale zdecydowanie podoba mi się twój pomysł! –

+1

Tak, trzeba utworzyć instancję klasy Trace; może to być statyczne lub lokalne dla każdego komponentu, który chciał go użyć. Funkcja Trace :: Enable przełącza wewnętrzną flagę w klasie Trace (instancja wyliczenia stanu). To działałoby bardzo podobnie do std :: hex pozwala std :: cout na wyprowadzanie w formacie szesnastkowym zamiast dziesiętnego. Ponieważ będziesz pisać operatora << możesz wesprzeć Trace :: Enable (lub dowolne inne specyfikatory stanu, takie jak gadatliwość i inne) – 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

Czy istnieje możliwość "włączenia" std :: endl do definicji log()? –

+0

Definicja 'log (x)' może być łatwo zastąpiona funkcją inline. Czy istnieje powód, aby używać tutaj definicji? –

+0

Odnośnie std :: endl: żaden, że znam. Jeśli chcesz mniej kodu, zawsze możesz użyć typu, na przykład _e. Regardig # define/inline: bez powodu. –

2

1. Jeśli używasz g ++, możesz użyć opcji -D, co pozwala kompilatorowi zdefiniować wybrane makro.

Definiowanie

Na przykład:

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

2. Zgadzam to nie jest eleganckie albo, tak aby uczynić go nieco ładniejszy:

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*/ 
} 

Że może używać jak printf:

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

3. Trzecie rozwiązanie łatwiejsze, a bardziej C++ i Unix jak przekazać argument do twojego programu, który ma być użyty - jak makro wcześniej - do zainicjowania konkretnej zmiennej (która może być globalną).

Przykład: $ ./myprogram -v

if(optarg('v')) static const verbose = 1; 
Powiązane problemy