Wie sehen eure Log- und Konfigklassen aus



  • Wie sehen eure Log- und Konfigklassen in euren Projekten vom Design her aus?

    Ich habe vor, in der Konfigklasse meine Daten per Default zu setzen und aber die Moeglichkeit zu bieten, diese Werte durch Setter veraendern zu koennen, entweder aus dem Programm oder auch aus einer Konfigdatei heraus. Das alles sind dann statische Methoden, da ich eh nie mehr als eine Instanz brauchen werde.

    Die Logklasse habe ich noch nicht, aber ich will dort einfach nur in eine Datei schreiben mit Timestamp, Warnlevel, Methode und Message.

    Ich habe schon Logklassen als Singleton gesehen, aber warum dies? Reicht es nicht die Methoden einfach statisch zu machen?

    Ich bin sehr neugierig wie ihr das so handhabt.



  • CppNeuland schrieb:

    Ich habe schon Logklassen als Singleton gesehen, aber warum dies? Reicht es nicht die Methoden einfach statisch zu machen?

    Wozu ne Log-Klasse wenn sie dann nur statische Funktionen hat? Dann gleich freie Funktionen.

    Singletons sind mMn. immer eine Krücke. Ein kleiner Vorteil gegenüber freien Funktionen ist aber, dass man wenigstens der Grossteil so implementieren kann dass es mit mehreren Objekten funktioniert. Und dann nur an einer kleinen Stelle hergeht und das ganze dadurch verkrüppelt dass man dann nur ein Objekt erzeugt.
    Wobei man auch statt nem Singleton einfach ein paar wenige globale Objekte so einer Logging-Klasse anlegen kann.
    z.B. für verschiedene Log-Channels, deren Log-Level man dann getrennt einstellen kann oder sowas.



  • Für Konfigs benutze ich normalerweise Boost.ProgramOptions. Bei kleinere Projekten bzw. am Anfang werden die defaults im Programmcode gesetzt, später in .ini-Dateien.

    CppNeuland schrieb:

    Ich habe schon Logklassen als Singleton gesehen, aber warum dies? Reicht es nicht die Methoden einfach statisch zu machen?

    Der einzige Unterschied zwischen statischen Methoden und Singletons ist, dass Singletons einen Zustand speichern können - das wird bei statischen Methoden zumindest schwierig. Mögliche Dinge die man speichern kann sind z.B. konfigurierte Dateinamen für die Logfiles und das aktuelle Loglevel.

    Es gibt gegen die Verwendung Singletons im Allgemeinen gute Gründe, z.B. die generierten Abhängigkeiten usw. Eine Logging-Klasse, die als reine Datensenke funktioniert, ist eine der Ausnahmen, wo Singletons ok sein können. Es gibt allerdings auch Fanatiker/Dogmatiker, für die jedes Singleton einfach erstmal schlecht ist, ganz ohne Gründe und Abwägungen 😉



  • Boost.ProgramOptions

    Für kleinere Projecte, bzw wo die anforderung an die Configs nicht so hoch sind, ne super Lösung.
    Wenn ich Konsolen-tools schreib, nehm ich die gern her.

    Hab ich ne GUI, ist die meist mit Qt, dann verwend ich da lieber QSettings. Nachteil, man hat naturelich die commandline options nicht bei, dafür isses komfortabler ....

    Für richtig grosse Projecte, aka mehrere Teams, kommen wir meist mit beiden nicht hin. dann hat man aber meist auch konkrete anforderung ans Format und das verhalten .... dann wirds meist was eigenes.

    Fürs logging bevorzuge ich Interfaces, die per parent beziehungen nach untem durchgereicht sind. Auch entwerfe ich plugin fähige Schnittstellen etc immer schon mit logging interface, so dass ich auch das loggen meiner plugins kontrollieren und zentral verwalten kann.
    So kann ich notfalls zur laufzeit von Datei auf system logs, oder auf nen eigenes fenster ... etc umstellen, und das getrennt für jedes modul.
    Ganz nuetzlich wenn man die Logg-ausgaben von nem verwendeten Modul "catchen" will, um z.b. mit eigenen Infos anzureichern ....
    Hat man aber eher selten.

    Lohnt natürlich nur bei grösseren projekten.

    Singletons für Logging faend ich ok, wenn man sicherstellen kann das kein anderes globales Object von der loggingschnittelle gebrauch macht. sonst hat man das lifecycle Henne - Ei Problem ...

    Ciao ...



  • Sehr interessante Infos, danke.

    Warum kann ich in einer statischen Klasse nix speichern? Ich kann doch über meine statischen Methoden Daten in den statische Members ablegen.



  • statische Member sind versteckte globale variablen ... also quasi salonfaehig gemachte globale variablen.
    funktioniert genau so wie ungebundende funktionen + globale variablen.

    du gewinnst also nichts.

    Also warum dann nicht gleich (nur) eine globale Instanz und daran (nichtstatische) Methoden aufrufen ?

    Magst du nun etwas mehr lifecycle controlle und besseren zugriff auf die Instanz bist du sofort wieder beim singleton ... weil singleton ist wiederum nix anderes als eine versteckte globale Instanz, salonfaehig gemacht ...

    Ciao ...



  • CppNeuland schrieb:

    Sehr interessante Infos, danke.

    Warum kann ich in einer statischen Klasse nix speichern? Ich kann doch über meine statischen Methoden Daten in den statische Members ablegen.

    Ja, kannst du.
    Allerdings bringt das einige Probleme, folgendes Beispiel:

    class log{static std::ifstream file; public: void write(std::string);};
    // andere Datei
    class global {public: global() {log::write("global created");}};
    global g;
    

    Die Konstruktionsreihenfolge ist nicht definiert, d.h. g versucht via write den ifstream zu benutzen, obwohl der noch gar nicht erzeugt wurde!
    Mit Singletons hat man da mehr Sicherheit.



  • Mit Singletons hat man da mehr Sicherheit.

    neee eigentlich auch nicht 🙂
    Wenn du mehrere globale Variablen hasst, die voneinander abhaengig sind, hasst du das Problem an der Backe ...
    2 Singletons kannst Du noch sicherstellen, das 1 aufgerufen wird, bevor 2 es verwendet ... aber beim runterfahren gibts dann Spass ...

    die beste Lösung IMHO ist nur eine globale Variable / Singleton, was die anderen Singleton-Kandidaten als Member haelt und kontrolliert (aka nach reihenfolge) hoch und runterfahren kann.
    Das erreichst aber auch ohne das als singleton nur mit der einen globalen Variable.

    Ciao ...



  • RHBaum schrieb:

    Mit Singletons hat man da mehr Sicherheit.

    neee eigentlich auch nicht 🙂
    Wenn du mehrere globale Variablen hasst, die voneinander abhaengig sind, hasst du das Problem an der Backe ...
    2 Singletons kannst Du noch sicherstellen, das 1 aufgerufen wird, bevor 2 es verwendet ... aber beim runterfahren gibts dann Spass ...

    die beste Lösung IMHO ist nur eine globale Variable / Singleton, was die anderen Singleton-Kandidaten als Member haelt und kontrolliert (aka nach reihenfolge) hoch und runterfahren kann.
    Das erreichst aber auch ohne das als singleton nur mit der einen globalen Variable.

    Es gibt Möglichkeiten, das Destructionproblem zu umgehen (s. z.B. Modern C++ Design).
    Am besten ist es aber, das zu vermeiden (ich spreche jetzt vom Zugriff bei Zerstörung)



  • Die Konstruktion, Vernichtung und Abhaengigkeiten hatte ich gar nicht auf dem Schirm, das macht natuerlich Sinn und spricht fuer ein Singleton.



  • CppNeuland schrieb:

    Die Konstruktion, Vernichtung und Abhaengigkeiten hatte ich gar nicht auf dem Schirm, das macht natuerlich Sinn und spricht fuer ein Singleton.

    Wie kommst du jetzt auf das? Vergiss Singletons.

    Nimm freie Funktionen:

    class logger; // deine allgemeine Loggingklasse, kein Singleton
    
    logger& log() { static logger l(std::cerr); return l; }
    
    // Jetzt hast du die Möglichkeit, irgendwann mal das zu ergänzen
    logger& other_log() { static std::ofstream out("prog.log"); static logger l(std::out); return l; }
    


  • Sieht auch nett aus, schön kompakt.



  • dingledong schrieb:

    [...]

    Nimm freie Funktionen:

    logger& log() { static logger l(std::cerr); return l; }
    

    Funktions-statische Objekte haben das selbe Problem mit der unspezifizierten Zerstörungsreihenfolge wie Singletons, also in der Hinsicht auch keine Verbesserung. Die Logger sind hier auch globale Objekte.


Log in to reply