QT und Templates



  • Hab ich noch nie verwendet. Ich verstehe auch nicht ganz was du meinst, weil SomeClassType bei dir das T gar nicht verwendet.


  • Mod

    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? 🙂


  • Mod

    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"

    👍


  • Mod

    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 auf data 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 😉


  • Mod

    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.



  • Ich suche doch nicht danach. Ich habe mein Backend und da ist das mit der Schnittstelle genau so definiert. Da kannst Du auch jedes policy-based-design nehmen und hast die gleiche Schnittstelle. Also ich kann mir da viele Anwendungsmöglichkeiten vorstellen. Backend hat Templates -> will ich darstellen. Wenn das unrealistisch ist, dann ist es unrealistisch, dass man Templates in C++ nutzt. 😉 Oder was missverstehe ich hier?

    Nochmal: Ich suche die Möglichkeit im UI verschiedene Ausprägungen (definiert durch Templateargumente) eines Templates darzustellen, deren Templateklassen dieselbe Schnittstelle definieren, wobei das Anzuzeigende vom gleichen Typ (std::string) für alle Templateklassen ist und es deswegen für die UI-Klasse kein Problem ist, auch die gesamte Palette der Templateklassen zu akzeptieren.

    Und ja, manche Templateklassen haben spezielle Methoden. Daher gehört die UI-Klasse ja teilweise auch spezialisiert - das betrifft aber nur wenige Methoden.



  • Vielleicht kann man das Problem mit einem anderen Beispiel illustrieren. Sagen wir meinetwegen, dass ich als Daten wirklich einfach nur vector<int> und vectorstd::complex habe. Nehmen wir jetzt meinetwegen zur Darstellung eine QListView, dort soll für den int-vector in jeder Zeile eine Zahl und bei der complex-Variante für jede Zeile die Vektorschreibweise für complex zur Anzeige genutzt werden.

    Dann ist die View natürlich "enttemplatisiert", da die Datenbefüllung ja über einen Erben von QAbstractListModel stattfindet.

    Aber das verschiebt das Problem ja nur: Wie löse ich das Problem, dass QAbstractItemModel beide vector zur Anzeige gestattet?

    Variante 1 wäre z.B.:

    class VectorModelBase : public QAbstractListModel
    {
        // definiert alles, was sowohl für vector<int> als auch vector<complex> klappt
    };
    
    class IntVectorModel : public VectorModelBase
    {
        // implementiert Methoden entsprechend des int-Parameters anders
    };
    
    class ComplexVectorModel : public VectorModelBase
    {
        // implementiert Methoden entsprechend des complex-Parameters anders
    };
    

    Logischer erscheint mir aber doch Variante 2, durch die wir noch mehr in die Base stecken können:

    template<typename VectorT>
    class VectorModelBase : public QAbstractListModel
    {
        // wie oben, aber jetzt kann auch std::vector<VectorT>* ein Attribut sein (oder wenn wir Controller/Model zusammenlegen, meinetwegen auch ohne *)
        // Vorteil: In der obigen Variante haben wir immer noch viel Code doppelt, der einfach auf vector<T> zugreift,
        // obwohl T hier für mancherlei Zugriff egal ist (kombinierbar mit CRTP, keine Frage)
    };
    
    class IntVectorModel : public VectorModelBase<int>
    {
        // Doppelcode fällt heraus, nur vector<int>-spezifischer Code ist notwendig
    };
    
    class ComplexVectorModel : public VectorModelBase<complex>
    {
        // ibid.
    };
    

    Obiges Modell ist vereinfacht. Feedback dazu hilft mir mit hoher Wahrscheinlichkeit auch in meinem Fall weiter. 🙂

    Mein tatsächlicher Anwendungsfall ist jetzt nur insofern anders, dass ich View und Model (bzw. in einem strikteren Sinne imo Controller) nicht trenne, weil diese Trennung in meinen Augen nur dann Sinn ergibt, wenn man einen Controller für unterschiedliche Views nutzen möchte, was in meinem Fall ausgeschlossen erscheint (vll. bin ich zu kurzsichtig; und natürlich gibt es für meinen Fall von QT kein Model). Daher fällt der Template-Parameter eben direkt aufs View an und nicht auf ein dazwischen geschobenes Model.

    Kann man damit besser arbeiten? Auch die Frage meine ich ernst, da ich ja sehe, wie schwer meine Abstraktionen verständlich zu sein scheinen. 😞


  • Mod

    Also erstnoch mal die Bemerkung, dass Templates nicht mit dem Moc funktionieren, du könntest evtl mit einer Basisklasse Dinge vererben...

    ... ich bin aber der Meinung, das es viel besser ist für UI zu daten die hat-ein beziehung zu wählen, als die ist-ein.

    Zurück zum Model, die Schnittstelle mit der dort Datenabgefragt werden ist:

    QVariant data(const QModelIndex &index, int role) const
    

    index.row() wäre jetzt der Index von deinem vector. Index.column würde die Spalte angeben, hier wohl immer 0.
    Um hier in dein Backend sinnvoll zu kommen, würde ich obiges Interface etwas umbauen, und mit std/boost::function als Callback verwenden.
    Die Klasse auf die der Callback zeigt, kann dann in deinem Backend den richtigen String/Variant zurückgeben.


Anmelden zum Antworten