Singleton Verstaendnisproblem



  • Hallo erst mal. Ich versuche eine Logging-Klasse zu implementieren und wollte dabei ein Singleton zu benutzen. Die Beschreibung in HumeSikkins Beitrag ist ja ok aber wie ist es mit Member-Variablen und anderen Methoden. Mussen/sollen sie auch static sein oder ist es unnoetig?

    class Log
    		{
    		private:
    			Log(void){}
    			Log(const Log& rhs);
    			Log operator=(const Log&);
    			enum LEVEL
    			{
    				ERROR       = 0xFF0000,
    				WARNING     = 0x0000FF,
    				OK          = 0x00FF00,
    				INFORMATION = 0xFFFFFF
    			}level;
    			template<typename T>
    			::std::string NumToString(const T& Num)
    			{
    				::std::string Res;
    				::std::ostringstream tmp;
    
    				tmp << Num;
    				Res = Num.str();
    
    				return Res;
    			}
    		public:
    			//level Err()const;
    			//void  Err(level ChangeCurrent);
    			static ::std::ofstream notify; // static oder nicht?
    			static Log& HtmlLog();
    			void Write(const ::std::string& message, ::std::ofstream& file); // static oder nicht?
    		};
    

    Wahre ueber Hilfen sei es Anregungen, Tips oder Kritik sehr Dankbar.



  • Normalerweise implementiert man eine Singleton-Klasse wie eine ganz normale Klasse und so, dass es auch mehrere davon geben könnte, die unabhängig voneinander arbeiten könnten (mal davon abgesehen, dass es unsinnig wäre - oder das Design falsch ist ^^) - und dann baut man sich ein Singleton-Grundgerüst als Template und lässt die richtige Klasse davon erben...

    also stark vereinfacht:

    /*singleton.h*/
    template <class T>
    class Singleton
    {
      static T;
    public:
      T* GetInstancePtr() {return T;};
    }
    
    /*beispiel.h*/
    
    #include "singleton.h"
    
    class Log : public Singleton <Log>
    {
    /*die implementation ganz normal*/
    }
    

    übrigens:

    Log(void){}
    //void in der Parameterliste mitzuschreiben ist in C++ sehr unüblich, also:
    Log(){}
    
    Log operator=(const Log&);
    /*warum noch mal kopieren? außerdem sollte man in den headern IMMER die varialben-Bezeichnung mitschreiben...
    weil man im header immer schön schnell nachgucken kann, um die Funktion grob zu verstehen...
    und arbeit macht es ja nun auch keine...*/
    Log& operator=(const Log& rhs);
    
    enum LEVEL
                {
                    ERROR       = 0xFF0000,
                    WARNING     = 0x0000FF,
                    OK          = 0x00FF00,
                    INFORMATION = 0xFFFFFF
                }level;
    

    also ich finds hässlich ^^ ich hoffe mal, dass das hier geht:

    enum Tlevel
    {
      error = 1,
      warning = 2,
      ok = 4,
     //das man noch platz für andere hätte 
      information = error | warning | ok
    }
    
    Tlevel level;
    

    falls du wert darauf legst, dass level unbedingt kein int ist, dann kannst du auch so was schreiben:
    enum Tlevel : unsigned char
    falls es aber auf jeden fall ein int sein soll, dann kann man das auch so machen: (oder halt auch mit dieser Art Vererbung, die aber so weit ich weiß, noch nicht von allen Compilern unterstützt wird...
    enum{/*...*/, force_int = 0xffffffff}

    template<typename T>
                ::std::string NumToString(const T& Num)
                {
                    ::std::string Res;
                    ::std::ostringstream tmp;
    
                    tmp << Num;
                    Res = Num.str(); //is übrigens falsch ^^ tmp.str() wäre richtig
    
                    return Res;
                }
    

    das hier hat nichts in ner Log-Klasse zu suchen... entweder, du legst nen Namespace Convert oder machst sie zumindest static in der log-klasse - obwohl es unsauber wäre...
    ich würds außerdem so schreiben:

    template<class T>
                ::std::string ToString(const T& in)
                {
                    ::std::ostringstream tmp;
                    tmp << in;
                    return tmp.str();
                }
    
    //level Err()const;
                //void  Err(level ChangeCurrent);
    

    Wie wärs statt dessen mit GetErrorLevel und SetErrorLevel?

    static ::std::ofstream notify; // static oder nicht?
    

    nicht static...

    static Log& HtmlLog();
    

    lass das weg, dafür haben wir das singleton ^^

    void Write(const ::std::string& message, ::std::ofstream& file); // static oder nicht?
    

    nicht static... warum willste den ofstream noch mal mit übergeben? ist es nicht der namens "notify"?

    bb



  • Ich will hier nicht den ewigen Glaubenskrieg wieder anzetteln, aber meiner Erfahrung nach ist es immer wieder sinnvoll ein paar Gedanken zu investieren ob es nicht sinnvoll ist anstatt eines Singleton z.B. einfach (teils anonyme) Namensraeume zu verwenden.



  • Ähh warum schreibts du eigentlich die ganze Zeit beispielsweise:

    ::std::string NumToString(const T& Num)
                {
                    ::std::string Res;
                    ::std::ostringstream tmp;
    

    Nicht nur das es furchtbar häßlich ist, es ist auch einfach unnötig. So ist es richtig:

    std::string NumToString(const T& Num)
                {
                    std::string Res;
                    std::ostringstream tmp;
    


  • Kurz nebenbei schrieb:

    Nicht nur das es furchtbar häßlich ist, es ist auch einfach unnötig. So ist es richtig:

    Ob das unnötig ist, kann man nur über den Kontext entscheiden, denn folgendes ist ein legales C++ Programm.

    #include <string>
    
    namespace myspace {
        namespace std {
            class string {}; // irgend eine eigene String Klasse
        }
    }
    
    int main () {
        namespace myspace {
            std::string name;
            ::std::string name2;
        }
    };
    


  • ~john schrieb:

    Ob das unnötig ist, kann man nur über den Kontext entscheiden, denn folgendes ist ein legales C++ Programm.

    Seit wann darf man eigene Namespaces mit Namen std einführen? Ich dachte, das wäre nur für Template-Spezialisierungen erlaubt. Oder bezieht sich das nur auf globale Namensräume?

    Abgesehen davon, wer würde so etwas tun? Kaum jemand, zumindest nicht mit gutem Grund. Und genau deshalb würde ich die :: am Anfang auch weglassen. Oder schreibst du das vor jeden Namensraum-Bezeichner?



  • Nexus schrieb:

    ~john schrieb:

    Ob das unnötig ist, kann man nur über den Kontext entscheiden, denn folgendes ist ein legales C++ Programm.

    Seit wann darf man eigene Namespaces mit Namen std einführen? Ich dachte, das wäre nur für Template-Spezialisierungen erlaubt. Oder bezieht sich das nur auf globale Namensräume?

    Abgesehen davon, wer würde so etwas tun? Kaum jemand, zumindest nicht mit gutem Grund. Und genau deshalb würde ich die :: am Anfang auch weglassen. Oder schreibst du das vor jeden Namensraum-Bezeichner?

    Nein, aber streng genommen müsste man es...
    du kannst deine namespaces nennen, wie du willst... (darfst nur im "äußeren" std nichts ändern), aber was ~john geschrieben hat, ist durchaus in ordnung...

    bb



  • Seit wann darf man eigene Namespaces mit Namen std einführen? Ich dachte, das wäre nur für Template-Spezialisierungen erlaubt. Oder bezieht sich das nur auf globale Namensräume?

    Sobald du was eignes in den namespace std einführst, gilt es nicht mehr als Standardkonformes Programm. Respektive die Standardbibliothek muss nicht mehr unbedingt konform arbeiten. Man überlege sich nur mal, dass man eine Klasse schreibt, die "zufälligerweise" den gleichen Namen hat, wie etwas, was in der std. lib benutzt wird und das überdeckt. Kann böse folgen haben.



  • myspace::std != ::std



  • Tachyon schrieb:

    myspace::std != ::std

    Ja, das ist klar. Habe nie das Gegenteil behauptet.

    btw:
    Als ich die Bezeichnung myspace gesehen habe, musste ich durchaus schmunzeln. 🙂



  • Nexus schrieb:

    Seit wann darf man eigene Namespaces mit Namen std einführen?

    Man darf "::std" nicht verändern, ich führe aber eine eigene String Klasse in "::myspace::std" ein, und das ist nun einmal nicht "::std". Die führenden "::" geben eine absolute Namensraumbezug an. D.h. "::std::string" ist garantiert die Stringklasse aus der ISO Norm, wohingegen "std::string" das nicht sein muß. Hier hängt es vom sogenannten Koenig-Lookup ab, welche Klasse genommen wird.

    Nexus schrieb:

    Abgesehen davon, wer würde so etwas tun? Kaum jemand, zumindest nicht mit gutem Grund. Und genau deshalb würde ich die :: am Anfang auch weglassen.

    In der Regel kann man das gefahrlos tun, aber es gibt Zusammenhänge, in denen dies notwendig ist. Zum Beispiel wenn man den new operator überlädt.



  • Okay, dann ist das Erstellen eigener Namensräume std also nur auf globaler Ebene nicht gestattet. Trotzdem kann ich mir nicht vorstellen, dass jemand aus Spass einen eigenen Namespace std (wenn auch innerhalb eines anderen, z.B. myspace ) erstellt, zumal ja auch die Möglichkeit von using namespace myspace; in Betracht gezogen werden muss.

    Halt wieder so ein Fall, wo man das :: eigentlich ruhig weglassen kann, und einem nur etwas passiert, wenn man absichtlich schlecht programmierte Konstrukte und böse Namensräume benutzt...



  • Nexus schrieb:

    ...Trotzdem kann ich mir nicht vorstellen, dass jemand aus Spass einen eigenen Namespace std ... erstellt, zumal ja auch die Möglichkeit von using namespace myspace; in Betracht gezogen werden muss...

    Du hast wohl noch nie mit Entwicklern gearbeitet die Abkürzen wo nur möglich. Dann ist std garkein so unwahrscheinliches Konstrukt (und zudem werden diese Entwickler auch using namespace in Mengen verwenden und dann über die "fehlerhaften" C++ Bibliotheken schimpfen, und das es ohne Namensräume und mit kriptischen Bezeichnern ja viel besser war).

    cu André



  • asc schrieb:

    Du hast wohl noch nie mit Entwicklern gearbeitet die Abkürzen wo nur möglich.

    Nein, nicht wirklich. Ich kenne zwar die Tendenz, möglichst kurze (aber nicht bereits vergebene) Bezeichner zu wählen.

    Aber wie kann man auf die Idee kommen, einen eigenen Namenraum std zu erstellen? Von mir aus irgendwelche kryptischen, nichts aussagenden Bezeichner, aber std ?

    asc schrieb:

    [...] und dann über die "fehlerhaften" C++ Bibliotheken schimpfen, und das es ohne Namensräume und mit kriptischen Bezeichnern ja viel besser war).

    Vielleicht wäre C etwas für die. 🙂



  • Nexus schrieb:

    Aber wie kann man auf die Idee kommen, einen eigenen Namenraum std zu erstellen? Von mir aus irgendwelche kryptischen, nichts aussagenden Bezeichner, aber std ?

    Die Abkürzung std kenne ich außerhalb von C++ stellvertretend für Standard. Nun schiebt einer vielleicht die Allgemeinen Programmteile in einen Namensraum, und dort halte ich die Abkürzung std nun wirklich nicht für Außergewöhnlich (Neben Core, Base...).

    Nexus schrieb:

    asc schrieb:

    [...] und dann über die "fehlerhaften" C++ Bibliotheken schimpfen, und das es ohne Namensräume und mit kriptischen Bezeichnern ja viel besser war).

    Vielleicht wäre C etwas für die. 🙂

    Ooooohhhhh jaaa. ;P



  • asc schrieb:

    Die Abkürzung std kenne ich außerhalb von C++ stellvertretend für Standard. Nun schiebt einer vielleicht die Allgemeinen Programmteile in einen Namensraum, und dort halte ich die Abkürzung std nun wirklich nicht für Außergewöhnlich (Neben Core, Base...).

    Naja, wenn das wirklich so ist... Jeder C++-Programmierer sollte eigentlich die Standardbibliothek kennen und somit wissen, dass std reserviert ist und dessen eigene Einführung früher oder später zu Namenskonflikten führt. Denken viele Programmierer so kurzfristig? Ich kann nicht wirklich nachvollziehen, dass man trotz allem einen eigenen Namensraum einführt, der sich mit bereits existierenden (und dann noch allgegenwärtigen) Namespaces überschneidet...



  • @unskilled
    Die Log-Klasse sollte eine html-Logdatei erstellen. Deswegen benutze ich bei den enum-Werten die Hexadezimale schreibweise. Bei dem

    operator=
    

    habe ich nur die Deklaration in der Header-File ohne die Definition in der .cpp Datei 😉 .

    Bei dem NumToString Template habe ich es so wegen der named return value optimization geschrieben. Bei der statischen Methode HtmlLog() hatte ich mich nach dem Meyer-Singleton orientiert. Nur habe statt

    static Singleton& GetInstanz()
            {
                // Die Instanz wird erst beim ersten Aufruf erzeugt.
                // Endet das Programm, wird Instanz vernichtet.
                static Singleton Instanz;
                return Instanz;
            }
    
    static Log& HtmlLog()
    {
         static Log Instance;
         return Instance;
    }
    

    geschrieben.

    @sqrt(-1)
    Da ich noch am Lernen bin(bitte nicht hauen) kann man mit anonymen Namensraeumen Singleton-Klassen oder globale Instanzen von Klassen ersetzen? Sind sie nicht auf die eine Uebersetzungseinheit beschraenkt?

    @Kurz nebenbei
    Werde es mir abgewoehnen. 😃

    thx fuer die Antworten.


Anmelden zum Antworten