Templates: undefined reference (g++-3.3 vs. g++-4.1) [gelöst]
-
rüdiger schrieb:
(ignore)
Leider kann ich mit dieser Antwort nichts anfangen.
Ich denke, das ich eine normale Frage gestellt habe oder ???
-
Mauze schrieb:
rüdiger schrieb:
(ignore)
Leider kann ich mit dieser Antwort nichts anfangen.
Ich denke, das ich eine normale Frage gestellt habe oder ???
Sorry, das bezog sich auf meinen Beitrag. Hatte Blödsinn geschrieben.
-
Einmal heißt das Template bei dir
TbaSingletonund einmalTbaSingleton_NoTrace. Und wenn Letzteres eine Klasse ist, wieso gibts du dafür Template-Parameter an?
-
Hatte ich gerade schon geändert, das hatte ich nur falsch hier eingestellt.
Der Code läuft seit einigen Jahren. Das mit dem _NoTrace hinter jeder Klasse ist so,
das ich durch den Präprozessor alles 2 mal erzeugen lasse. Einmal für "KLASSE" und einmal für "KLASSE_NoTrace".Die Programmierer die meine Libraryfunktionen verwenden, verwenden nie die _NoTrace Klasse und können zur Laufzeit einen Trace aktivieren,
der dann in einen Logfile schreibt oder der TCP-Server diesen Trace zur Verfügung stellt.Da ich für den TCP LogServer selber meine Library benutzen, dürfen diese Klassen ja zur Laufzeit keine Trace ausgeben - der Programmierer
will ja nur seinen Trace sehen. Darum werden alle Klassen 2 mal übersetzt.
-
Noch etwas Erklärung zum Code:
Das "template<class TYPE> class TbaSingleton" stellt die Basis für eine Singleton-Implementierung.
In dieser Klasse kann man mit GetObject() und ReleaseObject() auf das eigentliche Objekt zugreifen,
darum gibt es ein Objekt und einen Zähler für die Anzahl wie oft es jetzt verwendet wird:static unsigned int m_nUsageCounter; static TYPE* M_pObject;GetObject() und ReleaseObject() bearbeiten den Objektzähler. Damit dieser geschützt ist, ist dieser in einer CriticalSection bzw. Mutex.
Pro Singleton Klasse gibt dafür ein eigenes Objekt:static TbaCritSection m_csUsageCounterUnd genau dieses Objekt fehlt dem Linker dann:
template<> TbaCritSection TbaSingleton<TbaHostEnvLock>::m_csUsageCounter;Wenn ich das template<> bei g++-3.3 nicht benutze dann funktioniert es.
/* template<> */ TbaCritSection TbaSingleton<TbaHostEnvLock>::m_csUsageCounter;
-
Hallo zusammen
Ich habe das Problem mal auf ein minimum reduziert.
(Allerdings macht der Code keinen Sinn mehr)#include <cstdio> // H-FILE: WORKER CLASS class WorkerClass { public: void DoWork( void ) {;} }; // H-FILE: TEMPLATE FOR SINGLEION template<class TYPE> class Singleton { public: static void DummyFunc() { M_pObject = new TYPE(); m_Worker.DoWork(); }; private: static WorkerClass m_Worker; static TYPE* M_pObject; }; // H-FILE: MY CLASS class MyClass : public Singleton<MyClass> { public: friend class Singleton<MyClass>; WorkerClass m_CritSec; }; ////////////// // CPP-FILE // ////////////// // static members for my singleton class template<> WorkerClass Singleton<MyClass>::m_Worker; template<> MyClass* Singleton<MyClass>::M_pObject = NULL; int main( int argc, char** argv) { MyClass::DummyFunc(); }g++-4.x test.cpp liefert: test.cpp:(.text._ZN9SingletonI7MyClassE9DummyFuncEv[Singleton<MyClass>::DummyFunc()]+0x1a): undefined reference to `Singleton<MyClass>::m_Workerg++-3.3 test.cpp funktioniert, wenn der Code so geändert wurde (das Schlüsselwort template wurde entfernt): [b]/*** template<> ***/[/b] WorkerClass Singleton<MyClass>::m_Worker;Ich bin wirklich für jede Hilfe dankbar.
-
template<> WorkerClass Singleton<MyClass>::m_Worker;Das hier ist leider keine Definition sondern eine sogenannte nondefining declaration. Die ist für normale statische Klassenmember nicht erlaubt, bei voll spezialisierten statischen Membern von Klassentemplates aber schon. Das hat dummerweise zur Folge, dass statische Member von Klassentemplates, die nur einen default-Ctor haben, nicht vollständig spezialisiert werden können, wie du es hier machst.
Andererseits kann man ja immer die allgemeine default-initialisierte Definition mit dem Template Header mitliefern und bei Bedarf die Spezialisierung da anbringen, wo die default-Initialisierung nicht adäquat ist.Lösung daher:
// H-FILE: TEMPLATE FOR SINGLEION template<class TYPE> class Singleton { public: static void DummyFunc() { M_pObject = new TYPE(); m_Worker.DoWork(); }; private: static WorkerClass m_Worker; static TYPE* M_pObject; }; template <class TYPE> WorkerClass Singleton<TYPE>::m_Worker; template <class TYPE> Type* Singleton<TYPE>::M_pObject = 0;PS: dir ist klar dass du in DummyFunc() so jedesmal ein Speicherleck erzeugst? Die TYPEs werden nie zerstört...
-
pumuckl, fehlen da nicht noch die Rückgabetypen?
template <class TYPE> WorkerClass Singleton<TYPE>::m_Worker; template <class TYPE> Type* Singleton<TYPE>::M_pObject = 0;
-
Ja, danke für den Hinweis, habs reineditiert.
-
template<> WorkerClass Singleton<MyClass>::m_Worker = WorkerClass();
-
SUPER - DANKE
So funktioniert es jetzt:
template <class TYPE> WorkerClass Singleton<TYPE>::m_Worker; template<> MyClass* Singleton<MyClass>::M_pObject = NULL;Mein Problem war wohl, das ich nur alle paar Jahre Templates benutze.
Und wenn ich welche benutze, dann schaue ich immer nach wie ich es früher gemacht habe.
Gleichzeitig mit C++0x werde ich mir auch noch mal Templates anschauen müssen.In der zweiten Zeile verhält es sich wohl so wie bei integrierten Datentypen (e.g. int usw.), da ist immer noch meine alte Schreibweise OK.
Nochmals wirklich vielen Dank
Mauze
-
SUPER - DANKE
So funktioniert es jetzt:
template <class TYPE> WorkerClass Singleton<TYPE>::m_Worker; template<> MyClass* Singleton<MyClass>::M_pObject = NULL;Mein Problem war wohl, das ich nur alle paar Jahre Templates benutze.
Und wenn ich welche benutze, dann schaue ich immer nach wie ich es früher gemacht habe.
Gleichzeitig mit C++0x werde ich mir auch noch mal Templates anschauen müssen.In der zweiten Zeile verhält es sich wohl so wie bei integrierten Datentypen (e.g. int usw.), da ist immer noch meine alte Schreibweise OK.
Nochmals wirklich vielen Dank
Mauze
-
Mauze schrieb:
In der zweiten Zeile verhält es sich wohl so wie bei integrierten Datentypen (e.g. int usw.), da ist immer noch meine alte Schreibweise OK.
Jein. Das zweite funktioniert weil der Pointer nicht default-initialisiert wird.
Die beiden Definitionen gehören außerdem in zwei verschiedene Code-Teile und haben unterschiedliche Auswirkungen:
Das Template mit der Definition von m_Worker gehört in den Header mit dem Klassentemplate und sorgt dafür, das jede Instantiierung des Templates automatisch die entsprechende Definition des Members hat.
Die Spezialisierung der Definition von m_pObject gehört dagegen in den Teil, wo die entsprechende Spezialisierung von Sigleton benutzt wird (also in die .cpp), weil sie nur für die eine Instantiierung gilt. Außerdem muss die gleiche Definition für jede weitere Instantiierung von Singleton wiederholt werden.
Ich würde aus deinem Code heraus da keinen Grund für sehen und lieber auch gleich die Definition als template für alle Instantiierungen von Singleton mit in dessen Header packen.
-
Das ist jetzt der komplette fertige Code.
Zu beachten ist, das die entsprechenden Codeanteile in Wirklichkeit auch in den entsprechenden Dateien aufgeteilt werden.
Warum sage ich das, weil ich bei der Übernahme des Codes in mein wirkliches Projekt immer noch nicht linken konnte (weil aufgeteilt auf LIB, SO und EXE).Das Jein von Pumuckl mit der entsprechenden Erklärung hat mich dann in die Lage versetzt, mein Projekt endlich mit g++-4.3 linken zu können.
DANKE - DANKE - DANKE
#include <cstdio> // H-FILE: WORKER CLASS class WorkerClass { public: void DoWork( void ) {;} }; // H-FILE: TEMPLATE FOR SINGLEION template<class TYPE> class Singleton { public: static void DummyFunc() { M_pObject = new TYPE(); m_Worker.DoWork(); delete M_pObject; }; private: static WorkerClass m_Worker; static TYPE* M_pObject; }; template <class TYPE> WorkerClass Singleton<TYPE>::m_Worker; // H-FILE: MY CLASS class MyClass : public Singleton<MyClass> { public: friend class Singleton<MyClass>; WorkerClass m_CritSec; }; ////////////// // CPP-FILE // ////////////// template<> MyClass* Singleton<MyClass>::M_pObject = NULL; int main( int argc, char** argv) { MyClass::DummyFunc(); }Ich denke jetzt aber wirklich gelöst - ODER?
Mauze
-
Mauze schrieb:
Ich denke jetzt aber wirklich gelöst - ODER?
Immernoch jein. Wie oben schon gesagt musst du so immernoch für jede neue Intantiierung des Templates eine neue vollkommen spezialisierte Definition von M_pObject dazu packen, um dafür nicht undefined references zu kriegen.
Also nochmal die Frage: Was hindert dich daran, auch M_pObject im Header so zu definieren wie vorgeschlagen, nämlich per Template? Die Initialisierung mit NULL ist für alle Instantiierungen so doch völlig in Ordnung.
-
Das Forum war irgendwie nicht mehr erreichbar, darum jetzt erst eine Antwort.
Wenn ich das so wie von Dir vorgeschlagen versuchte, dann bekam ich immer folgenden Compiler Fehler:
error: expected constructor, destructor, or type conversion before ‘*’ tokenDann ist mir endlich der Tippfehler aufgefallen:
[b]FALSCH:[/b] template <class TYPE> [b]Type*[/b] Singleton<TYPE>::M_pObject = 0; [b]RICHTIG:[/b] template <class TYPE> [b]TYPE*[/b] Singleton<TYPE>::M_pObject = 0;Das ist jetzt der Code ohne JEIN:
#include <cstdio> // H-FILE: WORKER CLASS class WorkerClass { public: void DoWork( void ) {;} }; // H-FILE: TEMPLATE FOR SINGLEION template<class TYPE> class Singleton { public: static void DummyFunc() { M_pObject = new TYPE(); m_Worker.DoWork(); delete M_pObject; }; private: static WorkerClass m_Worker; static TYPE* M_pObject; }; template <class TYPE> WorkerClass Singleton<TYPE>::m_Worker; template <class TYPE> TYPE* Singleton<TYPE>::M_pObject = 0; // H-FILE: MY CLASS class MyClass : public Singleton<MyClass> { public: friend class Singleton<MyClass>; }; ////////////// // CPP-FILE // ////////////// int main( int argc, char** argv) { MyClass::DummyFunc(); }Mauze
-
Mauze schrieb:
Das ist jetzt der Code ohne JEIN:
Genau
