static-Pointer + Template

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • static-Pointer + Template

    Würde sowas nach dem motto

    Quellcode

    1. template<class T> class SGT
    2. {
    3. static T* m_pointer;
    4. public:
    5. SGT()
    6. {if (!m_pointer) m_pointer = new T();}
    7. };

    funktionieren / wird dann für jede verwendete Template-Klasse ein pointer angelegt oder muss ich mit accessviolations rechnen, weil m_pointer einen Pointer auf die erste verwendete Klasse T zeigt?

    Hintergrund: Wäre eine praktische sache um Singletons einfach zu handhaben und template-Programierung wollte ich immer schon mal ausprobieren
    There are only 10 types of people in the world: Those who understand binary, and those who don't.

    Download meines ersten Spiels:HIER
    Über Feedback würde ich mich freuen ;)
  • Ich habs mal versucht so zu probieren:

    Quellcode

    1. #include <iostream>
    2. template<class T> class Singleton
    3. {
    4. private:
    5. static T* m_pointer;
    6. public:
    7. Singleton();
    8. };
    9. template<class T> Singleton::Singleton()
    10. {
    11. if(!m_pointer)
    12. {
    13. m_pointer = new T();
    14. std::cout << "NEUER POINTER!" << endl;;
    15. }
    16. }
    17. int main()
    18. {
    19. Singleton<float> s_float;
    20. Singleton<int> s_int;
    21. Singleton<char> s_char;
    22. }
    Alles anzeigen


    Und erhalte dabei Folgende Fehlermeldung:


    "GCC/Eclipse" schrieb:

    Building file: ../main.cpp
    Invoking: GCC C++ Compiler
    g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"main.d" -MT"main.d" -o"main.o" "../main.cpp"
    ../main.cpp:13: Fehler: >>template<class T> class Singleton<< ohne Template-Parameter verwendet
    ../main.cpp:14: Fehler: ISO-C++ verbietet Deklaration von >>Singleton<< ohne Typ
    ../main.cpp:14: Fehler: Deklaration des Templates >>template<class T> int Singleton()<<
    ../main.cpp:6: Fehler: steht mit der vorherigen Deklaration >>template<class T> class Singleton<< in Konflikt
    ../main.cpp:6: Fehler: vorherige Deklaration >>template<class T> class Singleton<< einer Nicht-Funktion
    ../main.cpp:14: Fehler: steht mit Funktionsdeklaration >>template<class T> int Singleton()<< in Konflikt
    ../main.cpp: In function `int Singleton()':
    ../main.cpp:15: Fehler: >>m_pointer<< nicht deklariert (erste Verwendung dieser Funktion)
    ../main.cpp:15: Fehler: (Jeder nicht deklarierte Bezeichner wird nur einmal f"ur jede Funktion, in der er vorkommt, gemeldet.)
    ../main.cpp:20: Warnung: keine return-Anweisung in nicht void zur"uckgebender Funktion
    ../main.cpp:31:4: Warnung: no newline at end of file


    "VC8/Visual C++ 2005 Express" schrieb:

    .\main.cpp( 11 ) : error C2955: "Singleton": Für die Verwendung der template-Klasse ist eine template-Argumentliste erforderlich.
    .\main.cpp( 9 :( Siehe Deklaration von 'Singleton'
    .\main.cpp( 18 ) : error C2244: 'Singleton<T>::Singleton': Keine Übereinstimmung für Funktionsdefinition mit vorhandener Deklaration gefunden
    .\main.cpp( 8 :( Siehe Deklaration von 'Singleton<T>::Singleton'
    Definition
    'Singleton::Singleton(void)'
    Vorhandene Deklarationen
    'Singleton<T>::Singleton(void)'


    ---------- EDIT ----------

    Hm - Konstruktor jetzt direckt inline definiert - nur noch Linkerfehler

    Quellcode

    1. #include <iostream>
    2. template<class T> class Singleton
    3. {
    4. private:
    5. static T* m_pointer;
    6. public:
    7. Singleton(){
    8. if(!m_pointer)
    9. {
    10. m_pointer = new T();
    11. std::cout << "NEUER POINTER!" << "\n";
    12. }
    13. }
    14. };
    15. int main()
    16. {
    17. Singleton<float> s_float;
    18. Singleton<int> s_int;
    19. Singleton<char> s_char;
    20. }
    Alles anzeigen

    "GCC/Eclipse" schrieb:

    **** Build of configuration Debug for project TemplateTest ****

    make -k all
    Building file: ../main.cpp
    Invoking: GCC C++ Compiler
    g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"main.d" -MT"main.d" -o"main.o" "../main.cpp"
    Finished building: ../main.cpp

    Building target: TemplateTest.exe
    Invoking: GCC C++ Linker
    g++ -o"TemplateTest.exe" ./main.o
    ./main.o: In function `_ZN9SingletonIcEC1Ev':
    /cygdrive/p/Eclipse/TemplateTest/Debug/../main.cpp:(.text$_ZN9SingletonIfEC1Ev[Singleton<float>::Singleton()]+0x8): undefined reference to `Singleton<float>::m_pointer'
    /cygdrive/p/Eclipse/TemplateTest/Debug/../main.cpp:(.text$_ZN9SingletonIfEC1Ev[Singleton<float>::Singleton()]+0x23): undefined reference to `Singleton<float>::m_pointer'
    /cygdrive/p/Eclipse/TemplateTest/Debug/../main.cpp:(.text$_ZN9SingletonIiEC1Ev[Singleton<int>::Singleton()]+0x8): undefined reference to `Singleton<int>::m_pointer'
    /cygdrive/p/Eclipse/TemplateTest/Debug/../main.cpp:(.text$_ZN9SingletonIiEC1Ev[Singleton<int>::Singleton()]+0x22): undefined reference to `Singleton<int>::m_pointer'
    /cygdrive/p/Eclipse/TemplateTest/Debug/../main.cpp:(.text$_ZN9SingletonIcEC1Ev[Singleton<char>::Singleton()]+0x8): undefined reference to `Singleton<char>::m_pointer'
    /cygdrive/p/Eclipse/TemplateTest/Debug/../main.cpp:(.text$_ZN9SingletonIcEC1Ev[Singleton<char>::Singleton()]+0x1f): undefined reference to `Singleton<char>::m_pointer'
    collect2: ld gab 1 als Ende-Status zur"uck
    make: *** [TemplateTest.exe] Fehler 1
    make: Das Target >>all<< wurde wegen Fehlern nicht aktualisiert.
    Build complete for project TemplateTest

    "VC8/Visual C++ 2005 Express" schrieb:

    ------ Erstellen gestartet: Projekt: abc, Konfiguration: Debug Win32 ------
    Kompilieren...
    main.cpp
    Verknüpfen...
    main.obj : error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""private: static float * Singleton<float>::m_pointer" (?m_pointer@?$Singleton@M@@0PAMA)" in Funktion ""public: __thiscall Singleton<float>::Singleton<float>(void)" (??0?$Singleton@M@@QAE@XZ)".
    main.obj : error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""private: static int * Singleton<int>::m_pointer" (?m_pointer@?$Singleton@H@@0PAHA)" in Funktion ""public: __thiscall Singleton<int>::Singleton<int>(void)" (??0?$Singleton@H@@QAE@XZ)".
    main.obj : error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""private: static char * Singleton<char>::m_pointer" (?m_pointer@?$Singleton@D@@0PADA)" in Funktion ""public: __thiscall Singleton<char>::Singleton<char>(void)" (??0?$Singleton@D@@QAE@XZ)".
    P:\Projekte\Templates\abc\Debug\abc.exe : fatal error LNK1120: 3 nicht aufgelöste externe Verweise.
    Das Buildprotokoll wurde unter "file://p:\Projekte\Templates\abc\abc\Debug\BuildLog.htm" gespeichert.
    abc - 4 Fehler, 0 Warnung(en)
    ========== Erstellen: 0 erfolgreich, Fehler bei 1, 0 aktuell, 0 übersprungen ==========


    Ergebniss: Experiment gescheitert - Derartiger Code nicht möglich?
    There are only 10 types of people in the world: Those who understand binary, and those who don't.

    Download meines ersten Spiels:HIER
    Über Feedback würde ich mich freuen ;)
  • Thx mit VC8 gehts jetzt, Eclipse/GCC bringt noch folgenden Fehler

    "GCC" schrieb:

    **** Build of configuration Debug for project TemplateTest ****

    make -k all
    Building file: ../main.cpp
    Invoking: GCC C++ Compiler
    g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"main.d" -MT"main.d" -o"main.o" "../main.cpp"
    ../main.cpp:17: Fehler: zu wenige Templateparameterlisten
    ../main.cpp:17: Fehler: expected `,' or `;' vor >>=<<-Zeichen
    ../main.cpp:18: Fehler: zu wenige Templateparameterlisten
    ../main.cpp:18: Fehler: expected `,' or `;' vor >>=<<-Zeichen
    ../main.cpp:19: Fehler: zu wenige Templateparameterlisten
    ../main.cpp:19: Fehler: expected `,' or `;' vor >>=<<-Zeichen
    make: *** [main.o] Fehler 1
    make: Das Target >>all<< wurde wegen Fehlern nicht aktualisiert.
    Build complete for project TemplateTest
    There are only 10 types of people in the world: Those who understand binary, and those who don't.

    Download meines ersten Spiels:HIER
    Über Feedback würde ich mich freuen ;)
  • Fehlermeldungen halbiert jetzt kommt nur noch

    ../main.cpp:17: Fehler: zu wenige Templateparameterlisten
    ../main.cpp:18: Fehler: zu wenige Templateparameterlisten
    ../main.cpp:19: Fehler: zu wenige Templateparameterlisten

    ist auch nicht so schlimm, da ich das Mit DirectX unter Eclipse nicht zum laufen bring


    Meine Lösung falls es jemanden Interessiert:

    Quellcode

    1. template<class T> class Singleton
    2. {
    3. private:
    4. static T* m_pointer;
    5. public:
    6. Singleton() {if(!m_pointer)m_pointer = new T();}
    7. T* operator->() {return m_pointer;}
    8. T& operator*() {return *m_pointer;}
    9. };
    10. #define DECLARE_SINGLETON(Type) Type* Singleton<Type>::m_pointer = 0;
    Alles anzeigen
    There are only 10 types of people in the world: Those who understand binary, and those who don't.

    Download meines ersten Spiels:HIER
    Über Feedback würde ich mich freuen ;)
  • Nice :)
    Ich würde aber evtl. noch eine "atexit" funktion einhängen, die den Pointer am Schluss auch wieder freigibt...

    Quellcode

    1. template<class T> class Singleton
    2. {
    3. private:
    4. static T* m_pointer;
    5. static void __cdecl killYourself ()
    6. {
    7. delete m_pointer;
    8. m_pointer = NULL;
    9. }
    10. public:
    11. Singleton() {if(!m_pointer) { m_pointer = new T(); atexit (Singleton<T>::killYourself ); } }
    12. T* operator->() {return m_pointer;}
    13. T& operator*() {return *m_pointer;}
    14. };
    Alles anzeigen


    Tested mit VC2003
  • Was mir noch auffaellt ....
    Ohne template:

    Quellcode

    1. class X
    2. {
    3. private:
    4. static T mY;
    5. };


    damit ist doch X::Y nur deklariert, noch nicht definiert. Also irgendwo muesste in ner binaercode erzeugenden Einheit (.c .cpp) irgendwo noch X::Y = ....; oder so auftauchen, sonst gibts eh unresolved symbols.

    Also bei deiner Version muesste irgendwo Singleton<float>::m_pointer usw auftauchen. fuer alle verwendeten template auspraegungen auch.

    Problem ist das du deine statische Variable halt in ner klassendefinition nur deklarierst.

    Besser laeufts wenn du lokale variablen in Funktionen / Methoden verwendest, weil dort sind sie zwar auch gloabal, nur eben dann auch auf template/Klasse/Methode getrennt gesehen, dafuer brauchst sie aber nicht getrennt deklarieren und definieren ....

    Quellcode

    1. template<typename T>
    2. class Singelton
    3. {
    4. public:
    5. static T* getSingleton()
    6. {
    7. static T * mySingleton = new T; // wird nur einmalig bei erster verwendung ausgefuehrt
    8. return mySingleton;
    9. }
    10. }


    So funktioniert eigentlich Die Singelton geschichte, zum erzeugen und auch zugriff rufst du immer ein und die selbe methode an dem Singelton Wrapper auf.
    Statische variablen die lokal in funktionen deklariert werden, werden beim ersten Zugriff initialisiert, und dann nie wieder ....

    Dein delete versuch schaut auch irgendwie ungluecklich aus, c++ bietet bessere möglichkeiten. Reine Zeigervariablen haben keinen Destruktor. Von statischen klassen-variablen hingegen werden die destruktoren von c++ automatisch bei programmende aufgerufen (glaub noch nach dem atexit)
    Warum das ned nutzen, wenn dein Singelton unbedingt dnyamisch angelegt werden muss (keine ahnung wieso, aber das musst du wissen) .
    Auto delete bietet dir die STL an :

    Quellcode

    1. template<typename T>
    2. class Singelton
    3. {
    4. public:
    5. static T* getSingleton()
    6. {
    7. static std::auto_ptr<T> mySingleton(new T); // wieder nur einmalige Initialisation bei erster verwendung
    8. return mySingleton.get();
    9. }
    10. }


    Und schon wird das ding beim "Untergang" deines Programms korrekt geloescht.

    T* operator->() {return m_pointer;}
    T& operator*() {return *m_pointer;}
    };

    Bei nem Singelton find ich ned wirklich intuitiv.
    Das implementieren der operatoren impliziet bedeutet das du nen member von der klasse brauchst. Als fortgeschrittener c++ user weiss man aber, das Singletons immer was globales sind und damit eigentlich losgeloest von ner instanz sind.

    Quellcode

    1. Singelton<X> mysingleton;
    2. mysingleton->DoSomething;

    wuerde ich als "unerwartete Syntax" abtun.

    erwarten wuerd ich sowas wie:
    Singelton<X>::getSingleton()->DoSomething();

    Iss aber reine geschmackssache.

    Implementieren kannst deine operatoren auch, darfst halt nur ned mit ner Klassenvariable machen, sondern musst innerhalb deiner operatoren immer auf die Zentrale get methode des singletons zurueckgreifen, dann funktioniert es auch ...

    Ciao ...
  • "phax" schrieb:

    In der 3. Auflage im Kapitel 9.4.1.1 :)


    Tatsache ist bei der 4. immer noch so ;)

    Nur im Index fehlts^^


    ----
    Die möglichkeit mit Autoptr ist ansich nicht schlecht ich bin mir nur ziemlich sicher, dass die Methode von der Performance nicht ganz ideal ist - in der Spieleprogrammierung durchaus relevant - und ich mit dem bisherigen + makro weiter komme
    There are only 10 types of people in the world: Those who understand binary, and those who don't.

    Download meines ersten Spiels:HIER
    Über Feedback würde ich mich freuen ;)
  • Die möglichkeit mit Autoptr ist ansich nicht schlecht ich bin mir nur ziemlich sicher, dass die Methode von der Performance nicht ganz ideal ist - in der Spieleprogrammierung durchaus relevant - und ich mit dem bisherigen + makro weiter komme


    Der zugriff ueber die get methode ist dein einzigstes Problem an der stelle. Und da ist der Performance verlust wahrscheinlich kaum messbar. Wo oft holst du dir denn den zeiger vom Singleton neu.
    Alternativ kannst dir den Zeiger ja auch noch cachen, aber das waer mir ned ansatzweisse den aufwand wert.
    Das anlegen und zerstoeren des auto_ptrs selber wird unkritisch sein, da das aufn stack passiert, ausserdem globale Variable = beim programmstart vor der main erzeugt und nach dem exit funktionen wieder geloescht.
    Bei der ersten verwendung wird deine Klasse mit new erstellt und dem autoptr zugewiesen. die zuweisung ist trivial, dein performance problem sollte das new sein !

    Also bevor du hier ueber die performance von STL klassen nachdenkst, solltest bei deinem Design selber anfangen.
    Warum deine Singleton klasse dynamisch anlegen ???

    Wenn sie statisch anlegen kannst, kannst den konstruktor deiner singleton klasse auf den programm start verlegen .... und dir den ganzen Zauber mit dem std::auto_ptr eh sparen !!!

    Pinzipiell, wenn du wirklich so performante programme mit c++ schreiben willst, das es um ein oder 2 funktionsaufrufe doch ankommt oder zumindest auf der ebene mitdiskutieren willst, solltest du dich mit c++ grundlagen, vor allem lebenszyklen von variablen, Kosten von new / malloc, eigene Allokatoren ... derart auskennen, das es im Schlaf herbeten kannst.
    Solche Probleme mit dem Singleton duerftest ned haben, sonst schlaegt jeder der dein Programm in nachhinein nochmal auf performance analysiert, die haende uebern kopf zusammen !

    Ciao ...
  • Noch nen Allgemeiner hinweis zur Performance:

    80 : 20 Regel (viele sprechen gar von 90 : 10)

    80% deines Codes wird nur 20% der Laufzeit deines Programmes ausmachen. Start und Ende sind meist fuer die performance ned wirklich so relevant.
    Sprich einige wenige Codestuecke verheizen nen Grossteil deiner CPU leistung, der rest hindert den prozessor nur am einschlafen quasi ^^

    d.h. in der Praxis, wenn dir unsicher bist: Zuerst die sichere variante implementieren, wenn du das gefuehl hasst, dass genau das dein program bremst, dann profiler nehmen und genauer analysieren.

    Und von haus aus per definition unperformanten code meiden ... bei jedem new solltest du sofort einen inneren Schmerz spueren, der dich zumindest zum noch mal nachdenken bringt (man kann ned jedes new eliminieren)

    Ciao ...
  • @phax
    Siehe artikel oberhalb, warum dich mit ner membervariable an der stelle ned rumaergern solltest.

    Fuer nen dynamisch anzulegende SingletonKlasse sollte es eher so aussehen ....

    Quellcode

    1. template<class T> class Singleton
    2. {
    3. private:
    4. inline static T * getPointer()
    5. {
    6. static auto_ptr<T> m_pointer (new T ());
    7. return m_pointer.get();
    8. }
    9. public:
    10. inline T* operator->() {return getPointer();}
    11. inline T& operator*() {return *(getPointer());}
    12. };
    Alles anzeigen


    Fuer statische singletons dann so :

    Quellcode

    1. template<class T> class Singleton
    2. {
    3. private:
    4. inline T & getRef()
    5. {
    6. static T mRef(/* was man zur einmaligen initialisation braucht */);
    7. return mRef;
    8. }
    9. public:
    10. inline T* operator->() {return &getRef();}
    11. inline T& operator*() {return getRef();}
    12. };
    Alles anzeigen


    wie gesagt, ich kann ned verstehen warum das teil dynamisch angelegt werden muss,
    und warum man da mit zeigeroperatoren drauf muss (meiner Meinung nach ungewoehlich fuern singleton), das wird frueher oder spaeter aerger machen.

    Ciao ...
  • Es gibt imho kein Problem mit der Allokation per "new".
    Das Problem was ich eher sehe ist, dass die Typsierung über einen Typ nicht ausreicht. Bist du dir sicher, dass du nur ein Singleton vom Typ "int" haben willst???

    @Sussi: die Membervariable ist statisch und zerstört sich selbst. Ich versteh das Problem nicht...
    Das inline ist schön gut, aber nur eine Empfehlung an den Compiler. D.h. ein Compiler muss die Methode nicht inlinen, würde es in diesem Fall aber automatisch machen, da die Methoden keinerlei Schleifen etc. enthalten.

    Die Variante mit der statischen Methode ist natürlich viel geschickter und sollte auch so gemacht werden ("Meyer's Singleton").
    Um die Operator-Überladung zu verhindern würde ich folgendes vorschlagen:

    Quellcode

    1. template<class T> class Singleton {
    2. private:
    3. Singleton () {} // private um Instantiierung von außen zu vermeiden
    4. Singleton (const Singleton&);
    5. Singleton& operator = (const Singleton&);
    6. public:
    7. static T& getInstance () {
    8. static T Instance;
    9. return Instance;
    10. }
    11. };
    Alles anzeigen
  • @Sussi: die Membervariable ist statisch und zerstört sich selbst. Ich versteh das Problem nicht...

    Das Problem ist in diesem Fall nicht die construction und destruction ... sondern die trennung zwischen definition und deklaration ....

    Quellcode

    1. class X
    2. {
    3. static int Myvar = 5;
    4. };


    geht halt nicht !!! sondern muss ausserhalb definiert werden

    Quellcode

    1. class X
    2. {
    3. static int Myvar;
    4. };
    5. int X::Myvar = 5; // irgendwo in ner implementation

    auf templates umgemuenzt wuerde das eben zu diesen ungluecklichen makrodefinitionen fuehren, weil du muestest dann nicht nur die template Klasse typisieren, sondern auch die statische membervariable auch definieren. Das wuerd ich dem anwender nicht zumuten.
    Nur deshalb die funktion ! Damit man das ding auf ne lokale statische Variable bekommt, welche man in definieren und deklarieren innerhalb einer anweisung kann.

    Es gibt imho kein Problem mit der Allokation per "new"

    Wenn wer ueber performance redet, sollte er das normale new meiden. Das new "schadet" vielleicht nicht, aber ist es denn auch erforderlich ?

    Das inline klar, bei modernen compilern bringt das nix mehr, weil die wenns es ned koennen, ohne meldung nicht mehr inlinen, und wenn funktionen im header deklariert sind, wie es bei den templates eh meist der fall ist, werdens eh geinlinet, wenn nixtechnisches dagegen spricht.
    Aber es gibt auch ältere Compiler ... siehs eher als gewohnheit an, und als marker fuer code teile vom programmierer, wo er denkt das es auf speed ankommt.

    Ciao ...