Logger Class: Wie Verwendung maximal einfach?
-
böses Makro.
Was wenn ich
if(foo) LOG("dies"); else LOG("das");
schreiben will?
-
Shade Of Mine schrieb:
böses Makro.
Was wenn ich
if(foo) LOG("dies"); else LOG("das");
schreiben will?
Das war nur eine Skizze (deshalb "in der einfachsten Form") - was würdest Du denn vorschlagen, um den Gebrauch sicherer zu machen?
-
Werner Salomon schrieb:
Das war nur eine Skizze (deshalb "in der einfachsten Form") - was würdest Du denn vorschlagen, um den Gebrauch sicherer zu machen?
#define LOG(...) if(0)noop();else { ... } //oder #define LOG(...) ((void)doLog(...)
noop ist eine inline funktion die nichts tut und doLog wäre eine Funktion die den Code implementiert den du im Makro selber stehen hast.
Makros sind tricky und sollten deshalb wenn möglich nicht komplex sein.
-
Wie wäre es zumindest mit dem guten alten
do {...} while(0)
drumherum? Ansonsten würde mich erst einmal interessieren, was der Threadersteller auf deinen berechtigten Einwand von wegen clog zu antworten hat.
-
Die do {} while(0) und if(0) Varianten erzeugen Warnungen wegen den Konstanten Ausdrücken... nur so als Hinweis. Kann sehr mühsam sein...
-
von dem Makro halte ich nicht viel, außerdem geht dadurch auch die std::ostream-Syntax teilweise wieder den Bach runter. Ich hatte mir mal sowas überlegt (aber nie ausprobiert):
#include <iostream> #define CURRENT_LOGLEVEL 2 class Logger { bool doLog; Logger(bool b) : doLog(b) {} friend Logger log(std::size_t); public: template <class T> Logger const& operator<<(T const& t) const { if (doLog) std::clog << t; return *this; } ~Logger() { *this << '\n'; } }; Logger log(std::size_t level) { return Logger(level > CURRENT_LOGLEVEL); } int main() { log(2) << "2"; log(3) << 3; log(4) << '4' << " usw..."; }
-
pumuckl schrieb:
von dem Makro halte ich nicht viel, außerdem geht dadurch auch die std::ostream-Syntax teilweise wieder den Bach runter. Ich hatte mir mal sowas überlegt (aber nie ausprobiert):
Ich finde Makros sind die einzig sinnvolle Lösung.
Denn ich will nicht nur das Loggen was ich schreibe, sondern Metainformationen dabei haben. zB die aktuelle Uhrzeit, uU welches Modul, Funktion, Datei, etc. den Logeintrag generiert hat.Das meiste davon läuft nur mittels Makros. Die << Syntax ist dabei trivial zu behalten.
Deine Idee ist aber nett.
-
theta schrieb:
Die do {} while(0) und if(0) Varianten erzeugen Warnungen wegen den Konstanten Ausdrücken... nur so als Hinweis. Kann sehr mühsam sein...
Bei welchem Compiler?
while(true) {}
ist zB ein standard idiom für endlosschleifen.if(NDEBUG) oder so sind auch nicht wirklich unnatürlich. Da sollte ein Compiler nicht warnen.
-
Shade Of Mine schrieb:
theta schrieb:
Die do {} while(0) und if(0) Varianten erzeugen Warnungen wegen den Konstanten Ausdrücken... nur so als Hinweis. Kann sehr mühsam sein...
Bei welchem Compiler?
while(true) {}
ist zB ein standard idiom für endlosschleifen.if(NDEBUG) oder so sind auch nicht wirklich unnatürlich. Da sollte ein Compiler nicht warnen.
VS2010 SP1, Warning Level 4
int main() { do {} while(0); if(0) {} while(true) {} }
Bei allen drei:
warning C4127: conditional expression is constant
if(NDEBUG) {}
Kompiliert im Debug Mode gar nicht, weil NDEBUG nicht definiert ist. Ich weiss allerdings nicht, was der Standard dazu sagt.
-
Interessant. Den hab ich wohl aus guten Gründen aus.
Eine von vielen sinnlosen Warnungen vom VC++...Muss man wohl statt 0 einfach __LINE__==-1 setzen.
Und NDEBUG stimmt natürlich, ich hab eher an DEBUG_LEVEL und Co gedacht.
-
Shade Of Mine schrieb:
Ich finde Makros sind die einzig sinnvolle Lösung.
Denn ich will nicht nur das Loggen was ich schreibe, sondern Metainformationen dabei haben. zB die aktuelle Uhrzeit, uU welches Modul, Funktion, Datei, etc. den Logeintrag generiert hat.Das meiste davon läuft nur mittels Makros. Die << Syntax ist dabei trivial zu behalten.
Gut, unter dem Gesichtspunkt kann man das nur mit dem Makro machen. Dann vereinfacht sich mein kram sogar etwas:
#include <iostream> #define CURRENT_LOGLEVEL 2 template <bool doLog> struct Logger { template <class T> Logger& operator<< (T const& t) { return *this; } static Logger create(char const*, std::size_t, std::size_t) { return Logger(); } }; template <> struct Logger<true> { private: Logger(char const* file, std::size_t line, std::size_t level) { std::clog << "LOG(" << level << ')' << file << ':' << line << "> "; } public: template <class T> std::ostream& operator<<(T const& t) const { std::clog << t; return std::clog; } ~Logger() { std::clog << '\n'; } static Logger create(char const* file, std::size_t line, std::size_t level) { return Logger(file, line, level); } }; #define LOG(level) \ Logger<(level>CURRENT_LOGLEVEL)>::create(__FILE__, __LINE__, level) int main() { LOG(2) << "2"; LOG(3) << 3; LOG(4) << '4' << "usw..."; LOG(5) << "5"; }
Dürfte alles geinlined werden.
-
Was dieser Log-Level bringen soll, ist mir zwar gerade ein Rätsel, aber du hast den Include <cstddef> für size_t vergessen.
Warum nicht einfach sowas:
enum log_type { info, warning, error, fatal };
Oder so ähnlich.