QT und Templates
-
Hi,
nutzt ihr die beiden zusammen? Mein Backend soll auf Dauer jetzt viele Templates nutzen. Das führt aber dazu, dass einige UI-Elemente in bestem Falle auch Templates wären.
Das könnte man sich schon so hinbiegen, z.B. wie nachfolgend beschrieben, aber macht ihr das oder wie löst ihr das Problem?
class SomeClassUIBase { Q_OBJECT public: // ctor Q_SLOTS: void method() {methodImpl();} Q_SIGNALS: void someSignal(); protected: virtual void methodImpl(); }; //====== template<typename T> class SomeClassType : public SomeClassUIBase { public: // ctor. private: virtual methodImpl() { /* ... */} }; //====== // evtl. noch Spezialisierungen
Habe ich auch Mal ausprobiert. Wird ein abgeleiteter Typ erstellt, kann man signals connecten und Slots werden auf die Weise auch durchgereicht. Sogar
emit
funktioniert im Erbling.Ist jetzt halt unschön, dass man für QT noch eine Extramethode einbauen muss, wenn der Ableiter was anders als Base machen will. Geht's noch eleganter oder ist das so ok?
-
Dass sollte gehen, ich selber verwende da ganz gerne CRTP, damit bin ich von der Basisklasse was unabhängiger, und habe trotzdem noch die reale Klasse, nur dazwischen halt das Template.
-
Zwar sagt mir CRTP was und ich habe auch schon diverse Implementierungen gesehen, dennoch: Könntest Du kurz vielleicht skizzieren, wie Du das meinst?
-
Hab ich noch nie verwendet. Ich verstehe auch nicht ganz was du meinst, weil SomeClassType bei dir das T gar nicht verwendet.
-
Eisflamme schrieb:
Zwar sagt mir CRTP was und ich habe auch schon diverse Implementierungen gesehen, dennoch: Könntest Du kurz vielleicht skizzieren, wie Du das meinst?
CRTP (Curriously recurring template pattern) ist nützlich, wenn du bestimmte Eigenschaften vererben willst, aber dies innerhalb einer Vererbungshierachie tun musst. Aktuell vererbe ich damit die Eigenschaft dass ein QGraphicsItem eine Verbindung zu einem anderen haben kann.
Kurzform:
template<class derived, class base> class CRTP : public base { public: using base::base;//C++11 Konstruktoren von Base vererben virtual void foo(); void bar();//Eigenschaften welche "injeziert" werden sollen ... }; class Impl : CRTP<Impl, *insert baseclass*> { ... };
-
Hm, wo genau bringt das denn einen Vorteil ggü. dem Code in meinem Opening Post? Du hast eine Klasse mehr und dafür den Nicht-Template-Code nicht im Template. Bei mir stecke ich den Nicht-Template-Code aber einfach in die Base-Klasse, die ohnehin wegen QT kein Template sein kann.
Der signal/slot-Teil ist dann eben mit anderen Nicht-Template-Methoden gemischt, aber was ist daran schlechter als die CRTP-Lösung?
-
Eisflamme schrieb:
Hm, wo genau bringt das denn einen Vorteil ggü. dem Code in meinem Opening Post? Du hast eine Klasse mehr und dafür den Nicht-Template-Code nicht im Template. Bei mir stecke ich den Nicht-Template-Code aber einfach in die Base-Klasse, die ohnehin wegen QT kein Template sein kann.
Der signal/slot-Teil ist dann eben mit anderen Nicht-Template-Methoden gemischt, aber was ist daran schlechter als die CRTP-Lösung?
Meine Ausgangslage ist etwas anders als deine, ich habe verschiedene Klassen die bereits in Qt existieren, von denen ich erben tue.
Dann kann CRTP eine Lösung sein, dein Fall ist etwas anders, verstehe aber noch nicht genau, für was genau du deine Templateklasse einsetzen willst...
-
Eigentlich möchte ich meine QT-Klasse einfach über einen Typ customizen. Also wenn es ginge, würde ich einfach:
template<typename T> class SomeWidget : public QWidget { Q_OBJECT // ... };
schreiben, doch das ist ja nicht von QT unterstützt.
In einem genaueren Anwendungsfall greift SomeWidget auf ein Container-Template zu, dessen Typ durch T festgelegt wird. Der Benutzer übergibt SomeWidget dann einen Zeiger auf so einen Typ und fortan hat SomeWidget eine kennt-Beziehung zu diesem Objekt.
Okay und in Deinem Fall dient die CRTP-Klasse dazu, dass Du eben gewisse Eigenschaften/Methoden zu einer beliebigen QT-Klasse (bzw. einem Erben davon) hinzufügen möchtest? Coole Lösung, merk ich mir!
In meinem Fall ist das aber eben genau zugeschnitten, eigentlich sollte mein SomeWidget wie gesagt ein Template sein, was aber nicht geht. Das möchte ich dann gerne simulieren.
PS:
using base::base
ist ja geil, ich brauch mehr C++11-Unterstützung.
-
Nochmal, mir ist überhaupt nicht klar, was du erreichen willst. Ich hab das Gefühl, du willst jetzt einfach überall Templates verwenden
Was phlox meint, konnte ich nachvollziehen. Aber was du willst, nicht. Ich kann mir kaum einen Anwendungsfall vorstellen, wo es Sinn macht, der GUI einen Templateargument zu übergeben. Höchstens bei generischen Basisklassen, wie z.B. Models mit einer gewissen Basisfunktionalität.
-
Mechanics schrieb:
Ich kann mir kaum einen Anwendungsfall vorstellen, wo es Sinn macht, der GUI einen Templateargument zu übergeben. Höchstens bei generischen Basisklassen, wie z.B. Models mit einer gewissen Basisfunktionalität.
Templates sind Cool
Verwende sie viel und oft!Die höchste Form eines C++ Programmquellcodes besteht aus einer (1) Quellcodedatei, welche Main.cpp heißt und nur int main() enthält und 1000000 Templateheader. So ist man voll generisch und hipp.
Außerdem hat man beim Chef eine Ausrede, wenn man auf der Arbeit seinem Hobby nachgeht.
Chef: "Herr XY, warum sind sie nicht bei der Arbeit!?"
> "Das Programm kompiliert gerade"
Chef: "Das tut es doch schon seit letzter Woche"
> "Gut Ding will weile haben"
-
Eisflamme schrieb:
Eigentlich möchte ich meine QT-Klasse einfach über einen Typ customizen. Also wenn es ginge, würde ich einfach:
template<typename T> class SomeWidget : public QWidget { Q_OBJECT // ... };
schreiben, doch das ist ja nicht von QT unterstützt.
In einem genaueren Anwendungsfall greift SomeWidget auf ein Container-Template zu, dessen Typ durch T festgelegt wird. Der Benutzer übergibt SomeWidget dann einen Zeiger auf so einen Typ und fortan hat SomeWidget eine kennt-Beziehung zu diesem Objekt.
Das klingt für mich mehr danach als würdest du da eigentlich ein Model nutzen wollen, was dann verschiedene Daten enthalten kann.
Die UI Klasse sollte eigentlich keine Container mit Daten enthalten.
-
Ich habe einen ähnlichen Anwendungsfall.
Vereinfacht gesagt, habe ich eine Klasse, die einen Vector enthält, und ein Template Parameter an den Vector durch reicht.
Jetzt will ich diese Klasse in einem Widget darstellen. Also alle Einträge des Vectors in einer Liste, und bei einem Klick auf ein Item in dieser Liste, den entsprechenden Eintrag darstellen.
Ich wüsste jetzt nicht, wie ich das ohne Template eleganter angehen sollte.Musste mir mit entsprechenden #defines und Methoden, die in der Child Klasse ausgeführt werden, behelfen.
Bei Bedarf kann ich gerne mal ein Beispiel anhängen, sollte das nicht verständlich genug gewesen sein
-
Ich hab das Gefühl, du willst jetzt einfach überall Templates verwenden
Ne.
Wieso denken hier im Forum immer alle direkt schwarz-weiß?
anti-freaks Szenario klingt so ähnlich wie meins.
Statt einer Datenklasse habe ich ein Datentemplate, das erwartet also Templateparameter. Das UI soll unabhängig vom Templateparameter (!) diverse Daten anzeigen und diese Daten werden über verschiedene Methoden abgerufen.
template<typename T> class SomeWidget : public QWidget { Q_OBJECT typedef T dataType; private: T* data; public: SomeWidget(QWidget* parent, T* data) : QWidget(parent), data(data) {} void showSomeText() { // wie es dargestellt wird, ist ja fürs Beispiel erstmal egal QMessageBox::information(this, "Title", data->getText()); } };
Gut, ich könnte der Klasse auch einfach den Text übergeben. Aber zusätzlich gibt es noch Spezialisierungen von SomeWidget, weil für bestimmte Typen noch Extrainformationen angezeigt werden.
Das klingt für mich mehr danach als würdest du da eigentlich ein Model nutzen wollen, was dann verschiedene Daten enthalten kann.
Die UI Klasse sollte eigentlich keine Container mit Daten enthalten.Das Model ist ja ein Template, aber das UI soll das natürlich kennen, um davon die darzustellenden Daten zu extrahieren.
Alternativ könnte ich 10 Setter-Methoden in der UI-Klasse einführen und dann noch alle möglichen Darstellungs-Optionsflags einbauen. Das bläht aber in meinen Augen einfach nur den Code auf, sieht hässlich aus und ich sehe den Vorteil nicht. Außerdem soll die Klasse auch ändernd auf
data
zugreifen können. Dann müsste ich also ständig Signals feuern. Und beim Zugriff aufdata
soll SomeWidget aber wiederum Rückmeldung kriegen, ob z.B. das Einfügen irgendwelcher Daten geklappt hat oder so. Dann wird's noch bunter und ich brauche ein Vermittlerobjekt.MVC-technisch gesehen würdest Du also grundsätzlich empfehlen, V von C zu trennen, richtig? Ich sehe aber den Vorteil nicht, wenn die Datenklasse eh nicht austauschbar weil zu speziell ist. Dann kann ich doch V und C genau so gut zusammenlegen und spare mir den Vermittlungsaufwand. Nein?
-
anti-freak schrieb:
Ich habe einen ähnlichen Anwendungsfall.
Vereinfacht gesagt, habe ich eine Klasse, die einen Vector enthält, und ein Template Parameter an den Vector durch reicht.
Jetzt will ich diese Klasse in einem Widget darstellen. Also alle Einträge des Vectors in einer Liste, und bei einem Klick auf ein Item in dieser Liste, den entsprechenden Eintrag darstellen.
Ich wüsste jetzt nicht, wie ich das ohne Template eleganter angehen sollte.Das klingt eher das du ein Model haben möchtest. Durch ein Model (im falle von Qt als GUI Toolkit abgeleitet von QAbstractListModel) hast du folgende Vorteile:
(bezogen wieder auf Qt)- Du kannst QListView verwenden
- Deine Gui ist unabhängiger zur eigentlichen Datenstruktur
-
Eisflamme schrieb:
MVC-technisch gesehen würdest Du also grundsätzlich empfehlen, V von C zu trennen, richtig? Ich sehe aber den Vorteil nicht, wenn die Datenklasse eh nicht austauschbar weil zu speziell ist. Dann kann ich doch V und C genau so gut zusammenlegen und spare mir den Vermittlungsaufwand. Nein?
Im Falle von Qt kannst du durch die Verwendung eines Models vorhandene Views (z.b. QListView, QTreeView) verwenden, und musst dir diese nicht selbst basteln.
-
Ich habe ein QLineEdit, wo diverse Texte angezeigt werden können. Für bestimmte Templateparameter ist paintEvent(.) überschrieben und es wird noch auf der rechten Seite etwas zusätzlich angezeigt.
Alle Operationen, die mit der Logik des Textes bzw. des durch den Text repräsentierten Objekt zu tun haben, finden in einer eigenen Klasse bzw. in einem Template statt, da sitzt die ganze Logik.
Ich will jetzt nur noch den Braten anzeigen und auf Änderungen reagieren. Das könnte ich natürlich jetzt nochmal in eine Vermittler-(Controller)-Klasse auslagern - aber wozu? Der Aufwand noch von einem anderen UI-Objekt zu erben, um die Anzeige dort zu ermöglichen, ist doch genau so groß wie so eine Vermittlerklasse für jedes Objekt wie QLineEdit, QTextEdit und was-weiß-ich zu entwickeln. Ich habe einfach nur doppelt so viele Klassen, mehr Schreibaufwand und überhaupt nicht mehr Flexibilität, was nützt mir das denn?
Im Grunde genommen könnte man sogar so argumentieren, dass es so aussieht:
template<typename T> class Controller : public QLineEdit { };
, wobei jetzt QLineEdit meine UI-Klasse und Controller eben die Controller-Klasse ist. Der einzige Unterschied ist, dass Controller mit QLineEdit fest verdrahtet ist statt es über Komposition zu lösen. Aber bei Komposition hätte ich auch nicht mehr Flexibilität (bei Gegenmeinungen bitte skizzieren, ich sehe es leider nicht).
-
Ich sehe noch nicht, wie du das (sinnvoll) realisieren willst. Wie gesagt, GUI mit Templates stell ich mir schwierig vor. Willst du eine GUI bauen, die "generisch" irgendwelche Adressen anzeigen kann? Und dann AddressView<Contact> oder AddressView<Employee> oder sonst was erstellen und da drin greifst du auf T.name() zu? Sowas ist wohl kaum realistisch.
Und alle deine Codebeispiele hier deklarieren zwar template Argumente, benutzen sie aber nirgends. Wenn du ein sinnvolles Beispiel hast, her damit
-
Weil du vorher wegen CRTP fragtest:
CTRP würde die hier erlauben die Basisklasse auszutauschen. QLineEdit/QTextEdit oder andere UI Klassen problemlos möglich.Was ich jetzt allerdings nicht verstehe, wie passt ein LineEdit zu einem Container?
Was Model View angeht, das habe ich in meiner Einführung in Qt erwähnt.
-
Mechanics:
Klar, in dem Post oben http://www.c-plusplus.net/forum/p2349380#2349380 nutze ich das Template-Argument.Es gibt zwei Klassen, die man dort übergeben kann, die aber eben dieselbe Schnittstelle definieren, in diesem Fall meinetwegen getText(). Und wie oben erwähnt gibt es aber im Falle von meinetwegen Employee eine Spezialisierung der Klasse, wofür dann paintEvent() überschrieben wird.
Willst du eine GUI bauen, die "generisch" irgendwelche Adressen anzeigen kann? Und dann AddressView<Contact> oder AddressView<Employee> oder sonst was erstellen und da drin greifst du auf T.name() zu? Sowas ist wohl kaum realistisch.
Doch, im Grunde schon. Die müssen halt die richtige Schnittstelle definieren. Genau so, wie man vielen algorithm-Funktionen auch ein Iterator-Paar als Template-Argument übergeben kann und definiert sein muss, dass es begin() und end() gibt. Wieso ist das hier plötzlich nicht mehr realistisch?
phlox81:
Okay, dann habe ich dein CRTP-Beispiel richtig verstanden.Wie Models in QT funktionieren, weiß ich. Das nützt mir aber für den Fall nichts (überhaupt finde ich die QT-Modellklassen unglaublich schlecht zu handhaben, aber was soll's).
Für die Zerlegung des Containers in einen String werden die einzelnen Elemente in ihrer String-Repräsentation mit ',' konkateniert angezeigt. Wenn man etwas am String ändert, wird geparst und es wird wieder zurückverwandelt. Da das Parsen und Verwandeln in einen String durchaus für mehrere text-like QT-UI-Klassen passen soll, gibt es da natürlich eine Klasse, die das übernimmt. Aber die muss ja wohl dem QLineEdit bekannt sein. Da sich diese Texte aber wieder in diversen Eigenschaften unterscheiden (definiert durch Templateargument), muss die UI-Klasse zur Verwendung aller solcher Typen eben Template sein.
-
Eisflamme schrieb:
Wieso ist das hier plötzlich nicht mehr realistisch?
Weil es viel seltener vorkommt. Es ist unrealistisch, dass es viele Klassen gibt, die eine sehr ähnliche Schnittstelle haben und die man auf die gleiche Art und Weise darstellen kann. Du wirst in deinem Programm vielleicht mit Mühe und Not zwei Klassen finden, die da passen, und später kommt noch eine Methode hinzu, die du brauchst, und die andere Klasse hat die plötzlich nicht mehr.