Template-Wahnsinn - Designfehler?

Schnelle objektorientierte, kompilierende Programmiersprache.
Antworten
Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

Template-Wahnsinn - Designfehler?

Beitrag von Glocke » Sa Mär 02, 2013 11:48 am

Hi,

ich habe folgendes Problem: für die Netzwerkkommunikation möchte ich Clients vom Server accepten lassen und Worker erstellen, die sich um den Client kümmern. Dabei wollte ich zwei Klassen BaseServer und BaseWorker implementieren. Der Server muss die korrekte Worker-Klasse kennen, um die Worker von der richtigen (von BaseWorker abgeleiteten) Klasse zu erstellen. Daher habe ich mich dazu entschlossen, BaseServer einen Typparameter TWorker zu geben. Nun möchte ich, dass beim Erstellen des Workers ein Zeiger auf den zugehörigen Server mitgegeben wird, damit der Worker gegebenenfalls was am Server machen kann: z.B. sollte der Server den Worker-Thread nach dem Logout des Clients entfernen. Da dies vom Worker ausgeht (der Server weiß imho nicht wann das passiert, weil der Worker ja nur mit dem Client kommuniziert). Naja auf jeden Fall will ich BaseWorker einen Typparameter TServer mitgeben. Dann sieht der Quellcode nach meiner Idee so aus:

Code: Alles auswählen

#include <iostream>

template <typename TWorker>
class BaseServer {
    public:
        TWorker* worker; // eigentlich mehrere - hier vereinfacht
        BaseServer() {
            // Worker erstellen, der den Server kennt
            this->worker = new TWorker(this);
        }
};

template <typename TServer>
class BaseWorker {
    public:
        TServer* server;
        BaseWorker(TServer* server): server(server) {}
};

// --- jetzt die eigentlichen Klassen ---

class Server;

class Worker: public BaseWorker<Server> {
    public:
        // Konstruktor wird ja nicht vererbt
        Worker(Server* server): BaseWorker(server) {}
};

class Server: public BaseServer<Worker> {
    public:
        // Konstruktor wird ja nicht vererbt
        Server(): BaseServer<Worker>() {}
};

int main() {
    Server* server = new Server();
}

Und da sind wir auch schon beim Problem:
test.cpp: In Konstruktor »BaseServer<TWorker>::BaseServer() [mit TWorker = Worker]«:
test.cpp:33:38: instanziiert von hier
test.cpp:9:13: Fehler: ungültige Umwandlung von »BaseServer<Worker>* const« in »Server*« [-fpermissive]
test.cpp:27:9: Fehler: Argument 1 von »Worker::Worker(Server*)« wird initialisiert [-fpermissive]
Jetzt frage ich mich: mache ich bei der Verwendung der Templates etwas falsch, oder geht das - so wie ich es will - gar nicht bzw. ist nicht sinnvoll --> Designfehler?

Ich hoffe ihr könnt mir helfen - wahrscheinlich habe ich das ganze etwas zu konfus erklärt .. naja mal schauen was ihr sagt :mrgreen:

LG Glocke

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Template-Wahnsinn - Designfehler?

Beitrag von Xin » Sa Mär 02, 2013 9:16 pm

Glocke hat geschrieben:

Code: Alles auswählen

        BaseWorker(TServer* server): server(server) {}
Das geht erstmal nicht.
Glocke hat geschrieben:
test.cpp: In Konstruktor »BaseServer<TWorker>::BaseServer() [mit TWorker = Worker]«:
test.cpp:33:38: instanziiert von hier
test.cpp:9:13: Fehler: ungültige Umwandlung von »BaseServer<Worker>* const« in »Server*« [-fpermissive]
test.cpp:27:9: Fehler: Argument 1 von »Worker::Worker(Server*)« wird initialisiert [-fpermissive]
Jetzt frage ich mich: mache ich bei der Verwendung der Templates etwas falsch, oder geht das - so wie ich es will - gar nicht bzw. ist nicht sinnvoll --> Designfehler?
Ich gehe mal davon aus, dass Zeile 13 diese ist:

Code: Alles auswählen

            this->worker = new TWorker(this);
this ist vom Typ BaseServer< Worker >. Du möchtest jetzt eine Worker anlegen und übergibst dafür this - einen (BaseServer< Worker > *). Der Konstruktor von Worker will aber einen (Server *). Der ist weder const, noch reicht ihm ein (BaseServer< Worker > *). Server ist ja eine spezialisierte Form von BaseServer< Worker >, die viel mehr können könnte und viel mehr Daten mit sich führen könnte.

Du musst also entweder Worker gestatten sich mit BaseServer< Worker > zu begnügen, oder Du musst casten. dafür eignet sich das Curiously Recurring Template Pattern, über das fat-lobyte schon hier schrieb.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

Re: Template-Wahnsinn - Designfehler?

Beitrag von Glocke » So Mär 03, 2013 11:29 am

Xin hat geschrieben:this ist vom Typ BaseServer< Worker >. Du möchtest jetzt eine Worker anlegen und übergibst dafür this - einen (BaseServer< Worker > *). Der Konstruktor von Worker will aber einen (Server *). Der ist weder const, noch reicht ihm ein (BaseServer< Worker > *). Server ist ja eine spezialisierte Form von BaseServer< Worker >, die viel mehr können könnte und viel mehr Daten mit sich führen könnte.
Ja stimmt schon :?

Ich habe der BaseServer Klasse noch einen zweiten Typparameter TServer mitgegeben, damit sie beim erstellen des Workers sich selbst zur entsprechenden Server-Klasse casten kann:

Code: Alles auswählen

template <typename TWorker, typename TServer>
class BaseServer {
    public:
        TWorker* worker; // eigentlich mehrere - hier vereinfacht
        BaseServer() {
            // Worker erstellen, der den Server kennt
            this->worker = new TWorker((TServer*)this);
        }
};
Damit funktioniert das ganze jetzt wie geplant, allerdings muss ich den Server nun folgendermaßen ableiten:

Code: Alles auswählen

class Server: public BaseServer<Worker, Server> {
    public:
        // Konstruktor wird ja nicht vererbt
        Server(): BaseServer<Worker, Server>() {}
};
LG Glocke

Antworten