Forum: PC-Programmierung Template einer Template-Singleton-Klasse


von Marcel (Gast)


Lesenswert?

Hallo,

ich versuche eine Klasse als Kind einer Singleton-Basisklasse zu 
erstellen. Meine Kindklasse soll dabei wiederum als Templateklasse 
ausgeführt werden. Meine Singleton-Klasse sieht wiefolgt aus:
1
template <class T>
2
class TSingleton
3
{
4
  protected:
5
    // Membervariablen
6
    static T *m_pSingleton;    ///< Statisches Objekt
7
  public:
8
    /**
9
     * Destruktor
10
     */
11
    virtual ~TSingleton()
12
    {
13
    }
14
15
    /**
16
     * Get
17
     * Aufgabe: Wenn noetig, statisches Objekt erzeugen und Zeiger darauf zurueckgeben!
18
     */
19
    inline static T* Get()
20
    {
21
      // Existiert bereits eine Instanz?
22
      if(!m_pSingleton)
23
        m_pSingleton = new T;        // Nein, dann neue Instanz erzeugen!
24
25
      // Zeiger auf die Instanz zurueckgeben!
26
      return m_pSingleton;
27
    }
28
29
    /**
30
     * Statisches Objekt freigeben
31
     */
32
    static void Del()
33
    {
34
      // Existiert die Instanz?
35
      if(m_pSingleton)
36
      {
37
        delete m_pSingleton;        // Wenn ja, dann freigeben
38
        m_pSingleton = NULL;        // und Zeiger auf NULL setzen!
39
      }
40
    }
41
};
42
43
template <class T>
44
T* TSingleton<T>::m_pSingleton = 0;

Die Implementierung klappt auch soweit. Nun will ich jedoch, dass das 
Singleton-Objekt wiederum eine Templateklasse ist. Allerdings beschwert 
sich der Linker. Die Implementierung sieht dabei wiefolgt aus:

1
#include <QPixmap>
2
#define g_pSurface CSurfacemanager< QPixmap >::Get()
3
4
using namespace std;
5
6
template <class T>
7
class CSurfacemanager : public TSingleton<CSurfacemanager<T> >
8
{
9
    friend class TSingleton<CSurfacemanager<T> >;
10
11
  public:

Der Aufruf erfolgt über das Makro "g_pSurface". Der Linker beschwert 
sich allerdings, sobald ich eine Memberfunktion der Klasse aufrufen 
möchte:

Fehler: undefined reference to `CSurfacemanager<QPixmap>::QT_Render(int, 
SDL_Rect const*, SDL_Rect const*, QPixmap*)'

Weiß da wer weiter?

von Rolf M. (rmagnus)


Lesenswert?

Marcel schrieb:
> ich versuche eine Klasse als Kind einer Singleton-Basisklasse zu
> erstellen. Meine Kindklasse soll dabei wiederum als Templateklasse
> ausgeführt werden. Meine Singleton-Klasse sieht wiefolgt aus:

Den Sinn dieses Singleton-Templates verstehe ich nicht. Die Besonderheit 
bei einem Sigleton ist doch, daß es nur eine Instanz davon geben kann. 
Wie stellt denn TSingleton das für die Klasse T sicher?

> Fehler: undefined reference to `CSurfacemanager<QPixmap>::QT_Render(int,
> SDL_Rect const*, SDL_Rect const*, QPixmap*)'
>
> Weiß da wer weiter?

So ganz kapiere ich nicht, was du da machen willst. CSurfacemanger<T> 
ist also abgeleitet von TSingleton<CSurfacemanager<T> >? Warum 
eigentlich nicht von TSingleton<T>?

Prinzipiell heißt das ja, daß ein Aufruf von CSurfacemanager<T>::Get() 
dir einen Zeiger auf einen CSurfacemanager<T> zurückgibt, da das T in 
der Basisklasse ja einem CSurfacemanager<T> entspricht. Deshalb kommt 
die Fehlermeldung des Compilers.

: Bearbeitet durch User
von Marcel (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Marcel schrieb:
>> ich versuche eine Klasse als Kind einer Singleton-Basisklasse zu
>> erstellen. Meine Kindklasse soll dabei wiederum als Templateklasse
>> ausgeführt werden. Meine Singleton-Klasse sieht wiefolgt aus:
>
> Den Sinn dieses Singleton-Templates verstehe ich nicht. Die Besonderheit
> bei einem Sigleton ist doch, daß es nur eine Instanz davon geben kann.
> Wie stellt denn TSingleton das für die Klasse T sicher?

Ausgangssituation ist folgende: Ich will den Code der 
CSurfacemanager-Klasse doppelt benutzen, für 2 verschiedene Typen. 
(Einmal QPixmap, einmal SDL_Texture). Beide Spezialisierungen sollen 
dabei aber als Singleton-Objekt nutzbar sein.

>
>> Fehler: undefined reference to `CSurfacemanager<QPixmap>::QT_Render(int,
>> SDL_Rect const*, SDL_Rect const*, QPixmap*)'
>>
>> Weiß da wer weiter?
>
> So ganz kapiere ich nicht, was du da machen willst. CSurfacemanger<T>
> ist also abgeleitet von TSingleton<CSurfacemanager<T> >? Warum
> eigentlich nicht von TSingleton<T>?
>

Weil ich es meiner Meinung, genau von diesem Typ ableiten möchte. 
Dadurch stelle ich sicher, das meine beiden Singleton-Objekte auf 2 
verschiedene Klassen verweisen, einmal auf CSurfacemanger<QPixmap> und 
einmal auf CSurfacemanger<SDL_Texture>.

> Prinzipiell heißt das ja, daß ein Aufruf von CSurfacemanager<T>::Get()
> dir einen Zeiger auf einen CSurfacemanager<T> zurückgibt, da das T in
> der Basisklasse ja einem CSurfacemanager<T> entspricht. Deshalb kommt
> die Fehlermeldung des Compilers.

Aber mein Aufruf ist ja CSurfacemanager<QPixmap>::Get(), dieser liefert 
mir einen Zeiger auf CSurfacemanager<QPixmap> zurück und diese Klasse 
sollte ein CSurfacemanager<QPixmap>::QT_Render(int, SDL_Rect const*, 
SDL_Rect const*, QPixmap*) besitzen...

von tictactoe (Gast)


Lesenswert?

Die Fehlermeldung hat mMn nichts damit zu tun, dass die Basisklasse ein
TSingleton<irgendwas> ist.

Hast du etwa die Template-Klasse CSurfacemanger in eine Header-Datei und 
eine CPP-Datei aufgeteilt? Und die User von CSurfacemanager inkludieren 
nur die Header-Datei? Zeig mal die Definition von CSurfacemanager.

von Georg A. (georga)


Lesenswert?

Ist das gcc? AFAIR gibts da das Problem, dass die Templates von sich aus 
keinen Code erzeugen und man sie mit den tatsächlichen Typen deklarieren 
(und damit implementieren) muss, wenn das nicht in .h sondern .cpp 
steht.

D.h. entweder steckst du das Template in ein .h, oder du weisst schon 
alle Ts deiner Templates und schreibst sie zB. ans Ende des 
Singleton-cpps.

Ich hatte da auch mal einen schönen Link, der das Problem genauer 
beschrieben hat (trifft nicht jeden Compiler), find ihn aber nicht mehr 
:(

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Du willst die zukünftige Wartung deines Codes so schwierig wie möglich 
machen? Dann weiter so!

von Marcel (Gast)


Angehängte Dateien:

Lesenswert?

tictactoe schrieb:
> Die Fehlermeldung hat mMn nichts damit zu tun, dass die
> Basisklasse ein
> TSingleton<irgendwas> ist.
>
> Hast du etwa die Template-Klasse CSurfacemanger in eine Header-Datei und
> eine CPP-Datei aufgeteilt? Und die User von CSurfacemanager inkludieren
> nur die Header-Datei? Zeig mal die Definition von CSurfacemanager.

Genau, in der Headerdatei steht lediglich die Definition der Klasse, die 
deklaration von Funktionen findet in der cpp statt.

Meine Suche nach csurfacemanager.cpp in allen Projektdateien, liefert 
keine Ergebnisse. Die komplette Datei findest du im Anhang.

> Ist das gcc? AFAIR gibts da das Problem, dass die Templates von sich aus
> keinen Code erzeugen und man sie mit den tatsächlichen Typen deklarieren
> (und damit implementieren) muss, wenn das nicht in .h sondern .cpp
> steht.
>
> D.h. entweder steckst du das Template in ein .h, oder du weisst schon
> alle Ts deiner Templates und schreibst sie zB. ans Ende des
> Singleton-cpps.

Das verstehe ich nun nicht so ganz... Mein Singleton ist komplett in 
einer Headerdatei implementiert, dort findet keine aufteilung statt.


Für meinen Fall werde ich wohl erstmal so hingehen und meine 
Template-Klasse dadurch vereinfachen/abschaffen, das ich den konkreten 
Typen durch ein typdef festlege. Da ich ohnehin nur einen Typen in einer 
Umgebung benötige, kommt es letzlich aufs gleiche raus.

Würde trotzdem gerne wissen, woran es scheitert... Mein Gefühl sagt mir, 
ich hab irgendwo ein Problem mit der Syntax. Rein formell, sollte 
eigentlich nichts gegen die Idee/den Ansatz sprechen, den ich verfolgen 
wollte...

von Programmierer (Gast)


Lesenswert?

Marcel schrieb:
> Genau, in der Headerdatei steht lediglich die Definition der Klasse, die
> deklaration von Funktionen findet in der cpp statt

Genau das ist der Fehler. Alle template Funktionen müssen in den Header. 
Das ganze Konstrukt mit dieser Ableitung nennt sich übrigens "CRTP".

Marcel schrieb:
> #define g_pSurface CSurfacemanager< QPixmap >::Get()
Warum eine hässliche, gefährliche, Textersetzung, wenn man auch ein 
ordentliches typedef verwenden könnte?

von Bastler (Gast)


Lesenswert?

>> Fehler: undefined reference to `CSurfacemanager<QPixmap>::QT_Render(int,
>> SDL_Rect const*, SDL_Rect const*, QPixmap*)'

Das hat mit dem Singleton-Template nichts zu tun.

Wo hat du denn die Methode QT_Render implementiert?
In einer .hpp? Oder in eine .cpp, die nicht zum Projekt gehört?

BTW, wieso eigent QT_Render()? Sollte es, nach außen, nicht nur ein 
Render() geben? Oder ist das nur die interne Implementierung für QT?

Bitte nicht ärgern. Meiner Erfahrung nach lernt man weniger durch etwas 
gesagt zu bekommen, als durch mit Fragen zu Nachdenken gebracht worden 
sein.

von Marcel (Gast)


Lesenswert?

Programmierer schrieb:
> Marcel schrieb:
>> Genau, in der Headerdatei steht lediglich die Definition der Klasse, die
>> deklaration von Funktionen findet in der cpp statt
>
> Genau das ist der Fehler. Alle template Funktionen müssen in den Header.
> Das ganze Konstrukt mit dieser Ableitung nennt sich übrigens "CRTP".
>

Cool, das wars =)

> Marcel schrieb:
>> #define g_pSurface CSurfacemanager< QPixmap >::Get()
> Warum eine hässliche, gefährliche, Textersetzung, wenn man auch ein
> ordentliches typedef verwenden könnte?

Eigentlich berechtigter Einwand. Wie würde das denn konkret aussehen? 
Ich kann mir grade mit typdef nur eine Lösung vorstellen, die mich 
zwingt, das Get Manuell aufzurufen.

Bastler schrieb:
>>> Fehler: undefined reference to `CSurfacemanager<QPixmap>::QT_Render(int,
>>> SDL_Rect const*, SDL_Rect const*, QPixmap*)'
>
> Das hat mit dem Singleton-Template nichts zu tun.
>
> Wo hat du denn die Methode QT_Render implementiert?
> In einer .hpp? Oder in eine .cpp, die nicht zum Projekt gehört?
>
> BTW, wieso eigent QT_Render()? Sollte es, nach außen, nicht nur ein
> Render() geben? Oder ist das nur die interne Implementierung für QT?

Es gibt auch nur eine Render, QT_Render ist eigentlich intern und 
privat. Nachdem es mit dieser nicht geklappt hat, hatte ich das 
testweise vereinfacht. Das wird jetzt natürlich wieder zurückgebaut =)

von Rolf M. (rmagnus)


Lesenswert?

Marcel schrieb:
> Aber mein Aufruf ist ja CSurfacemanager<QPixmap>::Get(), dieser liefert
> mir einen Zeiger auf CSurfacemanager<QPixmap> zurück und diese Klasse
> sollte ein CSurfacemanager<QPixmap>::QT_Render(int, SDL_Rect const*,
> SDL_Rect const*, QPixmap*) besitzen...

Ach so. Ich dachte, du willst eine Memberfunktion von QPixmap aufrufen, 
nicht von CSurfacemanager<QPixpmap>.

Georg A. schrieb:
> Ist das gcc? AFAIR gibts da das Problem, dass die Templates von sich aus
> keinen Code erzeugen und man sie mit den tatsächlichen Typen deklarieren
> (und damit implementieren) muss, wenn das nicht in .h sondern .cpp
> steht.

Das ist kein Problem von gcc, sondern Teil von C++. Man kann das mit dem 
Schlüsselwort "export" lösen, und ein Problem von gcc (und den meisten 
anderen Compilern) ist, daß das nicht unterstützt wird.

Marcel schrieb:
>> D.h. entweder steckst du das Template in ein .h, oder du weisst schon
>> alle Ts deiner Templates und schreibst sie zB. ans Ende des
>> Singleton-cpps.
>
> Das verstehe ich nun nicht so ganz... Mein Singleton ist komplett in
> einer Headerdatei implementiert, dort findet keine aufteilung statt.

Und genau das mußt du mit allen templates machen.

> Würde trotzdem gerne wissen, woran es scheitert...

Das Template ist, wie der Name schon sagt, nur eine Vorlage für eine 
Klasse. Erst dort, wo T bekannt ist, kann auch Code für die Funktionen 
erzeugt werden. Dazu muß deren Quelltext aber an der Stelle bekannt 
sein, was nicht der Fall ist, wenn diese in einem anderen cpp-File 
definiert ist.

Programmierer schrieb:
> Marcel schrieb:
>> #define g_pSurface CSurfacemanager< QPixmap >::Get()
> Warum eine hässliche, gefährliche, Textersetzung, wenn man auch ein
> ordentliches typedef verwenden könnte?

Ein typedef für einen Funktionsaufruf? Ohne ein Makro fiele mir nur 
sowas ein:
1
struct { CSurfacemanager<QPixmap>* operator->() { return CSurfacemanager<QPixmap>*::Get(); } } g_pSurface;
Ob das jetzt schöner ist, darüber läßt sich sicher streiten.

: Bearbeitet durch User
von Marcel (Gast)


Lesenswert?

Hab das jetzt mit einer WrapperFunktion gelöst. Sollte zumindest 
typsicherer sein, als das define..

von Programmierer (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Man kann das mit dem Schlüsselwort "export" lösen,
Das ist seit 2011 in C++ abgeschafft...

Marcel schrieb:
> Ich kann mir grade mit typdef nur eine Lösung vorstellen, die mich
> zwingt, das Get Manuell aufzurufen.
Oohps, hab das ::Get übersehen.

Marcel schrieb:
> Hab das jetzt mit einer WrapperFunktion gelöst. Sollte zumindest
> typsicherer sein, als das define..
Ja, das ist die richtige Lösung.

von Marcel (Gast)


Lesenswert?

Sehr gut =) Problem ist gelöst, besten Dank für die Hilfe!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.