Hallo Leute,
ich komme zu meinem Lieblingsthema zurück. ;-) Ich bin bekanntermaßen mit dem Design der Bibliothek etwas unzufrieden und habe mir jetzt einen kleinen C++-Wrapper gestrickt. Da sich hier einige Leute für das Thema interessieren, will ich ihn mal vorstellen. Zuerst das übliche HelloButton-Beispiel:
#include "bollin.h"
#include "boost/bind.hpp"
int main(int argc, char** argv) { using Bollin::Application; Application application(argc, argv);
Bollin::PushButton button("&Quit"); button.onClick = boost::bind(&Application::quit, &application); application.setMainWidget(button); button.show();
return application.execute(); }
Benötigt wird neben meiner "Bollin"-Bibliothek die boost-Bibliothek. Der Quelltext ist fast selbsterklärend. Der Callback-Mechanismus ist reines C++, typsicher und viel flexibler als die QT-Variante. Zur Übersetzung meiner Bibliothek wird weiterhin moc benötigt. Anwender meiner Bibliothek brauchen aber kein moc mehr! Auch gibt es kein seltsames Pointer-Verhalten, alles wird in der Bibliothek gekapselt.
Das 2. Beispiel packt zwei PushButton in eine VerticalBox. Hier sieht man auch die große Flexibilität der Callbacks:
#include "bollin.h"
#include "boost/bind.hpp"
#include <iostream>
namespace {
void printHello() { std::cout << "Hello World!\n"; }
}
int main(int argc, char** argv) { using Bollin::Application; Application application(argc, argv);
Bollin::VerticalBox vbox; application.setMainWidget(vbox);
using Bollin::PushButton; PushButton helloButton("&Hello", vbox); helloButton.onClick = printHello; helloButton.setFocus();
PushButton quitButton("&Quit", vbox); quitButton.onClick = boost::bind(&Application::quit, &application);
vbox.show(); return application.execute(); }
Kommentare?
Torsten
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Thursday 18 April 2002 21:51, Torsten Werner wrote:
Kommentare?
<rumtroll> Warum nicht gleich die MFC nach Linux portieren? </rumtroll>
Konrad
- -- BOFH excuse #86:
Runt packets
Am Freitag, dem 19. April 2002 um 19:36:35, schrieb Konrad Rosenbaum:
<rumtroll> Warum nicht gleich die MFC nach Linux portieren? </rumtroll>
Mir ist nicht klar, was die Bemerkung mit meiner Nachricht zu tun hat.
*verwundert* Torsten
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
Hi,
On Friday 19 April 2002 20:23, Torsten Werner wrote:
Am Freitag, dem 19. April 2002 um 19:36:35, schrieb Konrad Rosenbaum:
<rumtroll> Warum nicht gleich die MFC nach Linux portieren? </rumtroll>
Mir ist nicht klar, was die Bemerkung mit meiner Nachricht zu tun hat.
button.onClick = boost::bind(&Application::quit, &application);
Der Kommentar war auf "onClick" bezogen, aber Du hast Recht, bei MFC ist das eine Methode, die man ueberschreiben muss.
Anderes Beispiel: MeinDialog *top=new MeinDialog; Bollin::PushButton *button=new Bollin::PushButton(mainwindow); //... button->onClick = boost::bind(&MeinDialog::close, top);
Ich habe trotzdem noch ein Problem damit: der MOC von Qt sorgt dafuer, dass alle Signal-Slot-Verbindungen von top aufgeloest werden, wenn ich das mache:
delete top;
Angenommen top ist eine Statusanzeige in einem eigenen Fenster und button ein Button im Hauptfenster, der die buttonlose Anzeige schliessen soll. Irgendwer koennte noch auf die Idee kommen zu klicken, wenn das Fenster schon zu ist. Ich bin leider nur ein Mensch, also werde ich Fehler der Kategorie "vergessene Code-Zeilen" machen.
Wie loest Du dieses Problem? Ich sehe nicht ganz, wie das funktionieren soll.
Ich bin eigentlich ganz gluecklich, wenn eine Bibliothek mir die ganze Routinearbeit abnimmt.
Problem zwei: was machst Du, wenn mehrere Objekte auf das Click-Ereignis dieses Buttons reagieren sollen? Bei Qt geht das recht einfach:
obj1.connect(button,SIGNAL(click()),SLOT(calcsomething())); obj2.connect(button,SIGNAL(click()),SLOT(showsomething()));
Typsicherheit halte ich bei Qt auch nicht gerade fuer ein Problem: 1. muessen alle Objekte von QObject abgeleitet sein, damit die Qt-Mechanismen funktionieren, ist diese Bedingung erfuellt funktioniert alles ohne Probleme. Ist sie nicht erfuellt beschwert sich der Compiler. 2. bekommt man schon beim Initialisieren der Objekte (sprich beim connect) mit, ob das Signal/der Slot existiert (via Debug-Meldung). Wer ein Programm nicht so weit testet, dass jede Code-Stelle mindestens einmal angefahren wurde, sollte sich ueberlegen warum er so viele Bug-Reports bekommt... 3. die semantischen Fehler, die jetzt noch uebrig sind wirst Du auch mit staerkerer Typpruefung nicht finden.
Konrad
- -- BOFH excuse #114:
electro-magnetic pulses from French above ground nuke testing.
Am Freitag, dem 19. April 2002 um 22:07:39, schrieb Konrad Rosenbaum:
Ich habe trotzdem noch ein Problem damit: der MOC von Qt sorgt dafuer, dass alle Signal-Slot-Verbindungen von top aufgeloest werden, wenn ich das mache:
delete top;
Ja klar, in meinem Beispiel würde so etwas schief gehen, aber auf der rechten Seite von
button.onClick =
kann alles stehen, auf das man in C++ ein () anwenden kann. D. h. auch ein Objekt, das korrekt aufräumt, wenn das eigentliche Objekt verschwindet. Natürlich ist meine Bibliothek auch noch nicht fertig.
Wie loest Du dieses Problem? Ich sehe nicht ganz, wie das funktionieren soll.
Hab im Moment kein Beispiel parat, mal sehen...
obj1.connect(button,SIGNAL(click()),SLOT(calcsomething())); obj2.connect(button,SIGNAL(click()),SLOT(showsomething()));
button.onClick = bind(ersteFunktion, bind(zweiteFunktion, _1));
beispielsweise oder auch beliebig kompliziertere Konstrukte
Typsicherheit halte ich bei Qt auch nicht gerade fuer ein Problem:
Ähem, ich kann problemlos einen slot(int a) mit einem signal(double b) verbinden und merke das nicht beim Übersetzen. Bei mir dagegen kann ich sogar so etwas machen:
object.onChange = bind(&Label::setText, &myLabel, bind<string, int>(lexical_cast, _1));
object.onChange() hat ein int-Argument und kann trotzdem den Text eines Labels ändern, obwohl diese Funktion einen std::string erwartet. Eingebaute Typkonvertierungen wie int->double bzw. solche mit existierenden Konstruktor/Konvertierer wie const char*->std::string gibt es bei mir natürlich "for free".
- muessen alle Objekte von QObject abgeleitet sein, damit die
Qt-Mechanismen funktionieren, ist diese Bedingung erfuellt funktioniert alles ohne Probleme. Ist sie nicht erfuellt beschwert sich der Compiler.
Genau damit ist keine saubere Trennung von Anwendungs- und GUI-Code möglich. Bei meinem Wrapper geht das selbstverständlich: reiner Anwendungcode braucht nicht den bollin.h-Header einzuziehen und natürlich wird auch kein moc benötigt.
Wer ein Programm nicht so weit testet, dass jede Code-Stelle mindestens einmal angefahren wurde, sollte sich ueberlegen warum er so viele Bug-Reports bekommt...
Damit findest du trotzdem nicht alle Fehler.
Torsten
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Saturday 20 April 2002 00:07, Torsten Werner wrote:
Am Freitag, dem 19. April 2002 um 22:07:39, schrieb Konrad Rosenbaum:
obj1.connect(button,SIGNAL(click()),SLOT(calcsomething())); obj2.connect(button,SIGNAL(click()),SLOT(showsomething()));
button.onClick = bind(ersteFunktion, bind(zweiteFunktion, _1));
beispielsweise oder auch beliebig kompliziertere Konstrukte
Da sagst du schon einen meiner Kritikpunkte: "kompliziert".
Was mache ich eigenlich, wenn sich die Assoziationen mittendrin veraendern. Z.B. ein Update-Button, an dem etwa zwei dutzend Elemente haengen, die sich dynamisch veraendern koennen (es kommen staendig einzelne Elemente dazu oder gehen weg, abhaengig von den Daten, die anfallen).
Typsicherheit halte ich bei Qt auch nicht gerade fuer ein Problem:
Ähem, ich kann problemlos einen slot(int a) mit einem signal(double b) verbinden und merke das nicht beim Übersetzen. Bei mir dagegen kann ich sogar so etwas machen:
object.onChange = bind(&Label::setText, &myLabel, bind<string, int>(lexical_cast, _1));
object.onChange() hat ein int-Argument und kann trotzdem den Text eines Labels ändern, obwohl diese Funktion einen std::string erwartet. Eingebaute Typkonvertierungen wie int->double bzw. solche mit existierenden Konstruktor/Konvertierer wie const char*->std::string gibt es bei mir natürlich "for free".
hmm, was fuer ein Ausdruck ist jetzt "bind<string, int>(lexical_cast, _1)"? Cast? Template? Wenn Template, dann lies Dir mal bitte durch, warum Qt keine Templates verwendet (doc/html/templates.html).
Ich nehme an, wenn ich <...> wegliese bekaeme ich einen Type-Mismatch - oder?
Was, wenn ich einen neuen Typ einfuehre, den bind noch nicht kennt? Muss ich dann bind ueberschreiben?
Was mache ich mit Konstrukten, wie diesem:
connect(SIGNAL(abc(int,int,QString,float)),obj1,SLOT(int,int)))
- ->bei Qt vollkommen legaler und funktionierender Code. Was muss ich bei Deiner Bibliothek machen, damit das funktioniert?
- muessen alle Objekte von QObject abgeleitet sein, damit die
Qt-Mechanismen funktionieren, ist diese Bedingung erfuellt funktioniert alles ohne Probleme. Ist sie nicht erfuellt beschwert sich der Compiler.
Genau damit ist keine saubere Trennung von Anwendungs- und GUI-Code möglich. Bei meinem Wrapper geht das selbstverständlich: reiner Anwendungcode braucht nicht den bollin.h-Header einzuziehen und
Wieso kommst Du auf sowas? Etwa ein Viertel der Qt-Klassen haben nichts, aber auch gar nichts, mit GUI zu tun, Beispiele:
QObject, QString, QDate, QDir, etc.pp.
Ich habe z.B. auch schon Qt-basierte Applikationen gebaut, ohne irgendetwas darzustellen (einen Daemon, um genau zu sein).
natürlich wird auch kein moc benötigt.
Hmm, wieso ist das ein Vorteil. So viel Zeit verbraet der doch gar nicht (im Vergleich zum eigentlichen Compiler).
Wer ein Programm nicht so weit testet, dass jede Code-Stelle mindestens einmal angefahren wurde, sollte sich ueberlegen warum er so viele Bug-Reports bekommt...
Damit findest du trotzdem nicht alle Fehler.
Weiss ich. Aber das ist schonmal ein wichtiger Teil dessen, was man allgemein "Unittest" nennt. Aber zumindest habe ich bei Qt dann bewiesen, dass alle echten Typfehler weg sind und dass der Code zumindest einigermassen korrekt laeuft, wenn man macht, was man soll. Was noch fehlt sind: Funktionstests, Integration-Tests, usw. und schliesslich Exception-Tests (fuer die werden, nach meiner Erfahrung, Tester am meisten gehasst und die dauern am laengsten).
Konrad
- -- BOFH excuse #134:
because of network lag due to too many people playing deathmatch
On Thu, Apr 18, 2002 at 09:51:05PM +0200, Torsten Werner wrote:
Hallo Leute,
ich komme zu meinem Lieblingsthema zurück. ;-) Ich bin bekanntermaßen mit dem Design der Bibliothek etwas unzufrieden und habe mir jetzt einen kleinen C++-Wrapper gestrickt. Da sich hier einige Leute für das Thema interessieren, will ich ihn mal vorstellen. Zuerst das übliche HelloButton-Beispiel:
?? Was genau hat Dich gestört? Ich fand dieses "Signal/Slot"-Gedöns so schlecht nicht. Irgendwie kam's mir immer ziemlich verständlich vor.
Einziger Wunsch wäre nach mehr Typsicherheit gewesen - so daß man schon zur Compiletime ein paar Fehler merkt, die sich sonst erst zur Runtime in irgendwelchen Error-Logs niederschlagen. Aber ich habe noch nicht sehr drüber nachgedacht - vielleicht geht's auch nicht viel anders, wegen des Dynamit?
meiner Bibliothek wird weiterhin moc benötigt. Anwender meiner Bibliothek brauchen aber kein moc mehr! Auch gibt es kein seltsames Pointer-Verhalten, alles wird in der Bibliothek gekapselt.
Was ist das seltsame Pointer-Verhalten?
Heiko
Am Freitag, dem 19. April 2002 um 20:20:08, schrieb Heiko Schlittermann:
?? Was genau hat Dich gestört?
Siehe auch die Mail an Konrad.
Was ist das seltsame Pointer-Verhalten?
Das automatische Aufrufen von delete für von mir selbst erstellten Objekten. Ich zitiere mal aus Alexandrescus "Modern C++ Design":
Good C++ libraries sport this interesting feature... At the other end of the spectrum are libraries that misuse silent C++ features (especially conversions and POINTER OWNERSHIP). They allow the user to type less, but at the cost of making dubious assumptions on the user's behalf.
QT gehört danach eindeutig zu den schlechten C++-Bibliotheken (Hervorhebung von mir).
Torsten
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Saturday 20 April 2002 00:16, Torsten Werner wrote:
Am Freitag, dem 19. April 2002 um 20:20:08, schrieb Heiko Schlittermann:
Was ist das seltsame Pointer-Verhalten?
Das automatische Aufrufen von delete für von mir selbst erstellten Objekten. Ich zitiere mal aus Alexandrescus "Modern C++ Design":
Hmm, wer ist Alexandrescu? Noch nie gehoert.
Good C++ libraries sport this interesting feature...
Welches Feature?
At the other end of the spectrum are libraries that misuse silent C++ features (especially conversions and POINTER OWNERSHIP). They allow the user to type less, but at the cost of making dubious assumptions on the user's behalf.
QT gehört danach eindeutig zu den schlechten C++-Bibliotheken (Hervorhebung von mir).
Meinetwegen mag es schlechtes C++ sein, aber Qt ist zuminedest gute OO.
C++ hat als Sprache zwei entscheidende Nachteile: 1. die starke Typisierung verhindert echte Polymorphie 2. es besitzt keinen Garbage Collector (der hilft wirklich sehr, bei komplexen Objekt-hierarchien/-netzen)
Der Signal-Slot-Mechanismus gibt mir einen grossen Teil der Polymorphie zurueck.
Das Object-Ownership-Model von Qt nimmt mir zumindest fuer Hierarchien die Garbage-Collection ab. Ich finde das sehr nett. Zugegeben fuer Netzwerke ist dieser Mechanismus hinderlich, aber die kommen in der Praxis nicht so haeufig vor, wie man befuerchten mag. Bisher habe ich jedenfalls immer einen eleganten Workaround gefunden.
Konrad
- -- BOFH excuse #267:
The UPS is on strike.
Am Samstag, dem 20. April 2002 um 11:24:29, schrieb Konrad Rosenbaum:
Da sagst du schon einen meiner Kritikpunkte: "kompliziert".
Mit kompliziert meinte ich nicht die Syntax, sondern die Datenstrukturen, die sich der Programmierer zur Lösung seines konkreten Problems überlegt hat. Das ist völlig unabhängig von der jeweiligen Programmiersprache. Ziel meines Wrappers ist es gerade, die Umsetzung möglichst einfach zu machen.
Was mache ich eigenlich, wenn sich die Assoziationen mittendrin veraendern. Z.B. ein Update-Button, an dem etwa zwei dutzend Elemente haengen, die sich dynamisch veraendern koennen (es kommen staendig einzelne Elemente dazu oder gehen weg, abhaengig von den Daten, die anfallen).
Hier hatte ich gestern Abend zu kurz nachgedacht. Die von mir verwendeten Callbacks sind richtige C++-Konstrukte mit Rückgabewerten (die im Einzelfall auch void sein KÖNNEN) im Gegensatz zu den QT-Slots. Damit ist dein Vergleich einer zwischen Äpfeln und Birnen. Oder wie willst du dein obiges Beispiel in plain Qt realisieren, wenn alle Elemente wichtige Daten zurückliefern?
Im übrigen ist ein weiterer Nachteil der Signal-Slot-Geschichte die Laufzeitperformance, weil es auf Zeichenkettenvergleichen beruht.
Dein gestriges Problem mit dem automatische Löschen der Callbacks lässt sich elegant lösen, wenn man nicht - wie ich in meinem Beispiel - mit Pointern, sondern entweder mit smart-Pointern oder direkten Kopien des Objekts arbeitet. Es ist also kein Problem, es sah in meinem Beispiel bloss so aus, als könnte es eins werden.
hmm, was fuer ein Ausdruck ist jetzt "bind<string, int>(lexical_cast, _1)"? Cast? Template?
Wegen <> sind bind und lexical_cast Templates. Was es mit _1 auf sich hat, muss ich nicht wissen, um es verwenden zu können.
Wenn Template, dann lies Dir mal bitte durch, warum Qt keine Templates verwendet (doc/html/templates.html).
Ja, die Begründung finde ich lustig: sie berufen sich darauf, dass sie auch alte Compiler unterstützen wollen. Als vor Jahren der Flamewar um QT in C++ versus plain C bei anderen Bibliotheken lief, war die Begründung, dass man für GUI unbedingt C++ bräuchte und weniger eben nicht ginge, basta! Jetzt, wo es gute Compiler gibt (selbst MS VC++ soll in der aktuellsten Version nicht so schlecht sein), benutzen sie es nicht.
Was, wenn ich einen neuen Typ einfuehre, den bind noch nicht kennt? Muss ich dann bind ueberschreiben?
Bind ist ein Template und kennt keine konkreten Typen. Du solltest dich unbedingt mal mit Templates in C++ beschäftigen, bevor du darüber schreibst!
connect(SIGNAL(abc(int,int,QString,float)),obj1,SLOT(int,int)))
- ->bei Qt vollkommen legaler und funktionierender Code.
Das ist kein legaler C++-Code, wenn die letzten beiden Argumente von abc keine Defaultwerte haben. Qt ist broken, wenn es das trotzdem tut. Wenn es aber Defaultwerte gibt, ist es legal und geht auch mit den von mir vorgestellten Konstrukten.
Wieso kommst Du auf sowas? Etwa ein Viertel der Qt-Klassen haben nichts, aber auch gar nichts, mit GUI zu tun, Beispiele:
Okay das ist ein anderes Thema, "Bollin" soll bloss ein GUI-Wrapper sein und nicht mehr als das leisten. Reiner Anwendungscode soll bei mir frei von bollin- bzw. Qt-Headern sein und das geht mit "Bollin".
Hmm, wieso ist das ein Vorteil. So viel Zeit verbraet der doch gar nicht (im Vergleich zum eigentlichen Compiler).
GCC 3.1 führt wohl für die ia64-Plattform ein standardisiertes C++-ABI ein. Sowas wie moc macht diese interessante Entwicklung wertlos.
Am Samstag, dem 20. April 2002 um 11:33:56, schrieb Konrad Rosenbaum:
Hmm, wer ist Alexandrescu? Noch nie gehoert.
Wurde kürzlich als bestes C++-Buch 2001 in comp.lang.iso-c++ gekürt.
Good C++ libraries sport this interesting feature...
Welches Feature?
Kapitel 5 ("Generalized Functors"), gemeint ist an der Stelle, dass es manchmal zu Uneindeutigkeiten kommt, die der Compiler nicht selbst auflösen kann und der Programmierer expliziten Code schreiben kann/muss. Wie z. B. oben:
bind<string, int>(lexical_cast, _1);
statt nur:
bind(lexical_cast, _1);
Aber das hat mit unserem Thema wenig zu tun.
- die starke Typisierung verhindert echte Polymorphie
... Der Signal-Slot-Mechanismus gibt mir einen grossen Teil der Polymorphie zurueck.
Das erscheint mir ohne Begründung als totaler Nonsens. Alexandrescu schreibt ein eigenes Kapitel über Multimethods (double dispatch) in C++. Mach das erst einmal in Java! Kennt Smalltalk so etwas? Im Buch werden CLOS, ML, Haskell und Dylan als Beispiele mit Sprachunterstützung für Multimethods aufgeführt.
- es besitzt keinen Garbage Collector (der hilft wirklich sehr, bei
komplexen Objekt-hierarchien/-netzen)
C++ hat hier eine andere Vorgehensweise, die einen Garbage Collector meines Erachtens unnötig machen. Der einzige echte Nachteil ist, dass das Anfängern etwas schwer fällt.
Im Übrigen ist deine Kritik wenig hilfreich. "Bollin" soll ein dünner und angenehm zu benutzender Wrapper für Qt sein. Wer gern auf die Qt-Mechanismen durchgreifen will, kann das tun. Die Bibliothek verbietet so etwas nicht bzw. macht das noch nicht einmal schwer.
Torsten
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Saturday 20 April 2002 13:33, Torsten Werner wrote:
Am Samstag, dem 20. April 2002 um 11:24:29, schrieb Konrad Rosenbaum:
Was mache ich eigenlich, wenn sich die Assoziationen mittendrin veraendern. Z.B. ein Update-Button, an dem etwa zwei dutzend Elemente haengen, die sich dynamisch veraendern koennen (es kommen staendig einzelne Elemente dazu oder gehen weg, abhaengig von den Daten, die anfallen).
Hier hatte ich gestern Abend zu kurz nachgedacht. Die von mir verwendeten Callbacks sind richtige C++-Konstrukte mit Rückgabewerten (die im Einzelfall auch void sein KÖNNEN) im Gegensatz zu den QT-Slots. Damit ist dein Vergleich einer zwischen Äpfeln und Birnen. Oder wie willst du dein obiges Beispiel in plain Qt realisieren, wenn alle Elemente wichtige Daten zurückliefern?
a) Ein weiteres Signal in der Gegenrichtung werfen. b) Parameter als Referenzen uebergeben.
Aber eigentlich sollte das nicht noetig sein, weil Signal-Slot Ereignisse abbildet und die sind nur in eine Richtung definiert. Nach dem Prinzip der Kapselung sollte es den Signaller nicht interessieren, ob es eine Ereignisbehandlung gibt.
"Der Vulkan bricht halt aus, ob jemand wegrennt ist ihm egal." ;-)
Im übrigen ist ein weiterer Nachteil der Signal-Slot-Geschichte die Laufzeitperformance, weil es auf Zeichenkettenvergleichen beruht.
Lt. Qt-Docu etwa 10-facher Aufwand eines einfachen Funktionsaufrufes. Da Ereignisse relativ selten sind (verglichen mit Warteschleifen und mathematischem Code) kann ich damit leben.
Dein gestriges Problem mit dem automatische Löschen der Callbacks lässt sich elegant lösen, wenn man nicht - wie ich in meinem Beispiel - mit Pointern, sondern entweder mit smart-Pointern oder direkten Kopien des Objekts arbeitet. Es ist also kein Problem, es sah in meinem Beispiel bloss so aus, als könnte es eins werden.
Smart Pointer klingt gut. Aber Kopien koennten ein Problem werden.
hmm, was fuer ein Ausdruck ist jetzt "bind<string, int>(lexical_cast, _1)"? Cast? Template?
Wegen <> sind bind und lexical_cast Templates. Was es mit _1 auf sich hat, muss ich nicht wissen, um es verwenden zu können.
Wenn Template, dann lies Dir mal bitte durch, warum Qt keine Templates verwendet (doc/html/templates.html).
Ja, die Begründung finde ich lustig: sie berufen sich darauf, dass sie auch alte Compiler unterstützen wollen. Als vor Jahren der Flamewar um QT in C++ versus plain C bei anderen Bibliotheken lief, war die Begründung, dass man für GUI unbedingt C++ bräuchte und weniger eben nicht ginge, basta! Jetzt, wo es gute Compiler gibt (selbst MS VC++ soll in der aktuellsten Version nicht so schlecht sein), benutzen sie es nicht.
Templates haben noch einen weiteren Nachteil: jede Object-Datei erzeugt eine neue Instanz des Codes. Nur wenige Linker (der von GNU z.B. nicht) koennen soetwas wieder reduzieren.
Was, wenn ich einen neuen Typ einfuehre, den bind noch nicht kennt? Muss ich dann bind ueberschreiben?
Bind ist ein Template und kennt keine konkreten Typen. Du solltest dich unbedingt mal mit Templates in C++ beschäftigen, bevor du darüber schreibst!
Ich weiss, was Templates sind. Es war eine Frage, kein Vorwurf. Ich war davon ausgegangen, dass es eine einfache Funktion ist.
connect(SIGNAL(abc(int,int,QString,float)),obj1,SLOT(int,int)))
Ups, das sollte SLOT(def(int,int)) heissen.
- ->bei Qt vollkommen legaler und funktionierender Code.
Das ist kein legaler C++-Code, wenn die letzten beiden Argumente von abc keine Defaultwerte haben. Qt ist broken, wenn es das trotzdem tut. Wenn es aber Defaultwerte gibt, ist es legal und geht auch mit den von mir vorgestellten Konstrukten.
Wieso? Einem Signal gibst Du Parameter mit, die der Slot aufnehmen kann oder auch nicht. Wenn ein Slot nicht an Paramer 3 und 4 interessiert ist, dann brauch er den doch nicht abfangen muessen, nur weil irgendein dummes Signal den Parameter liefern koennte.
Beispiel: QLineEdit implementiert ein Signal textchanged(QString). QButton (und damit QCheckBox) implementiert toggled(bool). Angenommen ich baue eine Art Formular und will nur wissen, ob Daten veraendert wurden, dann brauche ich bei Qt nur diese beiden Signal mit einem SLOT(changed()) zu verbinden, wohin sich etwas geaendert hat ist mir sch***egal, ich will ja nur den Save-Button aktivieren.
Wieso kommst Du auf sowas? Etwa ein Viertel der Qt-Klassen haben nichts, aber auch gar nichts, mit GUI zu tun, Beispiele:
Okay das ist ein anderes Thema, "Bollin" soll bloss ein GUI-Wrapper sein und nicht mehr als das leisten. Reiner Anwendungscode soll bei mir frei von bollin- bzw. Qt-Headern sein und das geht mit "Bollin".
Hmm, ich finde im Anwendungs-/Daten-modell soetwas wie Signal-Slot eigentlich auch sehr nuetzlich. Bekomme ich in einem Chat-Client z.B. eine Anfrage von einem anderen Nutzer kann ich das sehr elegant mit Qt-Mechanismen durch meine Blacklist-Filter jagen, bevor ich irgendetwas an die GUI durchreiche.
Hmm, wieso ist das ein Vorteil. So viel Zeit verbraet der doch gar nicht (im Vergleich zum eigentlichen Compiler).
GCC 3.1 führt wohl für die ia64-Plattform ein standardisiertes C++-ABI ein. Sowas wie moc macht diese interessante Entwicklung wertlos.
Was bitte hat ein Application Binary Interface mit einem Preprocessor zu tun?
Willst Du jetzt auch Embedded SQL verbieten?
Am Samstag, dem 20. April 2002 um 11:33:56, schrieb Konrad Rosenbaum:
Hmm, wer ist Alexandrescu? Noch nie gehoert.
Wurde kürzlich als bestes C++-Buch 2001 in comp.lang.iso-c++ gekürt.
Aha, danke.
Good C++ libraries sport this interesting feature...
Welches Feature?
Kapitel 5 ("Generalized Functors"), gemeint ist an der Stelle, dass es manchmal zu Uneindeutigkeiten kommt, die der Compiler nicht selbst auflösen kann und der Programmierer expliziten Code schreiben kann/muss. Wie z. B. oben:
bind<string, int>(lexical_cast, _1);
statt nur:
bind(lexical_cast, _1);
Aber das hat mit unserem Thema wenig zu tun.
Hmm. Muss ich mir bei Gelegenheit mal ansehen, im Moment verstehe ich diesen Code nicht. :-(
- die starke Typisierung verhindert echte Polymorphie
... Der Signal-Slot-Mechanismus gibt mir einen grossen Teil der Polymorphie zurueck.
Das erscheint mir ohne Begründung als totaler Nonsens. Alexandrescu schreibt ein eigenes Kapitel über Multimethods (double dispatch) in C++. Mach das erst einmal in Java! Kennt Smalltalk so etwas? Im Buch werden CLOS, ML, Haskell und Dylan als Beispiele mit Sprachunterstützung für Multimethods aufgeführt.
1. Java ist ebenso eine stark typgebundene Sprache wie C++.
2. AFAIK wurde double dispatch von SmallTalk _erfunden_! Unter anderem, weil SmallTalk nur drei Sprachkonstrukte kennt: Objekte/Methoden, Methoden-Aufrufe und Variablen. Double Dispatch macht einige weitere Konstrukte (wie z.B. switch) ueberfluessig.
- es besitzt keinen Garbage Collector (der hilft wirklich sehr, bei
komplexen Objekt-hierarchien/-netzen)
C++ hat hier eine andere Vorgehensweise, die einen Garbage Collector meines Erachtens unnötig machen.
Bitte jetzt keinen Flamewar ausbrechen lassen....
<Einschub> Fuer alle Anfaenger/Nicht-Programmier-Experten, die sich wundern, was hier los ist: es gibt zwei Philosophien unter Programmierern a) Speicherverwaltung ist zu schwierig/nervend, um sie dem Programmierer aufzuhalsen (Garbage Collector Befuerworter) b) Speicherverwaltung ist zu schwierig, um sie einem (dummen) Rechner zu ueberlassen (GC Gegner).
GC's haben Vorteile: * man braucht sich nicht selbst um die Freigabe von Speicher zu kuemmern * (theoretisch) kann ein Programm keine Speicherloecher mehr haben
und Nachteile: * sie tendieren dazu immer dann etwa 20s lange Saeuberungszyklen einzulegen, wenn man die Prozessorzeit fuer etwas anderes braucht * sie verbrauchen relativ viel Ressourcen (sowohl Speicher, als auch CPU-Zyklen) </Einschub>
Der einzige echte Nachteil ist, dass das Anfängern etwas schwer fällt.
Ich bin zwar kein Anfaenger mehr, aber bei den komplexen Modellen, die ich in den letzten Jahren angefangen habe aufzubauen faellt mir die Speicherverwaltung zu oft auf die Fuesse. Ich habe begonne GC's zu moegen.
Im Übrigen ist deine Kritik wenig hilfreich. "Bollin" soll ein dünner und angenehm zu benutzender Wrapper für Qt sein. Wer gern auf die Qt-Mechanismen durchgreifen will, kann das tun. Die Bibliothek verbietet so etwas nicht bzw. macht das noch nicht einmal schwer.
Hmm, ich dachte Du wolltest Kommentare?
Ich versuche nur potentielle Schwachstellen aufzuzeigen, sag' Bescheid, wenn ich aufhoeren soll, dann schreib ich lieber Code als eMails.
Konrad
- -- Suffocating together ... would create heroic camaraderie. -- Khan Noonian Singh, "Space Seed", stardate 3142.8
Am Samstag, dem 20. April 2002 um 16:48:35, schrieb Konrad Rosenbaum:
"Der Vulkan bricht halt aus, ob jemand wegrennt ist ihm egal." ;-)
Das ist natürlich ein Argument, gegen das schwer anzukämpfen ist. Ich bin halt der Meinung, dass es prinzipiell möglich sein muss, den Vulkan zu stoppen oder was auch immer man noch Vulkanen antun kann.
Lt. Qt-Docu etwa 10-facher Aufwand eines einfachen Funktionsaufrufes. Da Ereignisse relativ selten sind (verglichen mit Warteschleifen und mathematischem Code) kann ich damit leben.
Ich möchte aber den Mechanismus aber auch für mathematischen Code einsetzen und dann soll er schnell sein.
Aber Kopien koennten ein Problem werden.
"Konjunktive sollten prinzipiell immer vermeidbar sein."
Templates haben noch einen weiteren Nachteil: jede Object-Datei erzeugt eine neue Instanz des Codes. Nur wenige Linker (der von GNU z.B. nicht) koennen soetwas wieder reduzieren.
Der GNU-Linker kann das seit dem ELF-Binärformat. Für andere Plattformen implementierte schon der egcs den -frepo workaround.
[Template vs. Funktion:]
Ich war davon ausgegangen, dass es eine einfache Funktion ist.
bind ist ein Funktionstemplate. ;-)
connect(SIGNAL(abc(int,int,QString,float)),obj1,SLOT(int,int)))
Ups, das sollte SLOT(def(int,int)) heissen.
- ->bei Qt vollkommen legaler und funktionierender Code.
Das ist kein legaler C++-Code, wenn die letzten beiden Argumente von abc keine Defaultwerte haben. Qt ist broken, wenn es das trotzdem tut. Wenn es aber Defaultwerte gibt, ist es legal und geht auch mit den von mir vorgestellten Konstrukten.
Ups, meine Bemerkung mit den Defaultargumenten passt hier gar nicht.
Wieso? Einem Signal gibst Du Parameter mit, die der Slot aufnehmen kann oder auch nicht. Wenn ein Slot nicht an Paramer 3 und 4 interessiert ist, dann brauch er den doch nicht abfangen muessen, nur weil irgendein dummes Signal den Parameter liefern koennte.
Das ist aber extrem fehleranfällig. Stell dir vor ich entferne die Parameter, weil ich der Meinung bin, ich bräuchte sie gar nicht. Normalerweise würde mich der Compiler dafür ohrfeigen, aber bei Qt kann ich nur hoffen, dass die Dummheit zur Laufzeit auffällt. Wenn es in 99 % der Fälle glatt läuft, habe ich ein Problem, den Fehler zu finden. Ergo, mir gefällt die statische Typprüfung in C++.
Was bitte hat ein Application Binary Interface mit einem Preprocessor zu tun?
Eine Klasse mit Q_OBJECT sieht in Wirklichkeit ganz anders aus, als es der Quelltext vermuten lässt -> ganz anderes Binary Interface. Das führt eventuell auch bei anderen Sachen zu Problemen, Stichwort: persistente Objekte.
Willst Du jetzt auch Embedded SQL verbieten?
Ich weiss gar nicht, was das ist. Wenn es elegantere und standardkonformere Lösungen als Embedded SQL gibt, würde ich es aber vermutlich auch nicht sehr mögen.
bind(lexical_cast, _1);
Hmm. Muss ich mir bei Gelegenheit mal ansehen, im Moment verstehe ich diesen Code nicht. :-(
Was mir an boost besonders gefällt, ist die Tatsache, das sie einem peer review unterliegt und das Mitglieder des C++-Standardisierungsgremiums dazu gehören. Das lässt auf langfristige Ausrichtung und Portabilität hoffen.
- Java ist ebenso eine stark typgebundene Sprache wie C++.
Aber Java kennt genausowenig wie C++ ohne Bibliothekunterstützung Multidispatch. In C++ kann man aber die Unterstützung komplett in Bibliotheken kapseln.
- AFAIK wurde double dispatch von SmallTalk _erfunden_!
Meine Quellen erwähnen das gar nicht, vielmehr erscheint es mir so, als käme das vom CLOS. Da ich SmallTalk aber nicht kenne, will ich mich lieber nicht streiten.
Ich versuche nur potentielle Schwachstellen aufzuzeigen, sag' Bescheid, wenn ich aufhoeren soll, dann schreib ich lieber Code als eMails.
So habe ich es auch nicht gemeint. Die Diskussion geht mir etwas am Thema meiner Bibliothek vorbei, aber ich finde sie trotzdem nützlich.
Torsten
On Saturday 20 April 2002 21:53, Torsten Werner wrote:
Das ist natürlich ein Argument, gegen das schwer anzukämpfen ist. Ich bin halt der Meinung, dass es prinzipiell möglich sein muss, den Vulkan zu stoppen oder was auch immer man noch Vulkanen antun kann.
Zu DDR-Zeiten hätte man eine Mauer drum gebaut und das dann zum Standard erklärt :)
Das ist aber extrem fehleranfällig. Stell dir vor ich entferne die Parameter, weil ich der Meinung bin, ich bräuchte sie gar nicht. Normalerweise würde mich der Compiler dafür ohrfeigen, aber bei Qt kann ich nur hoffen, dass die Dummheit zur Laufzeit auffällt. Wenn es in 99 % der Fälle glatt läuft, habe ich ein Problem, den Fehler zu finden. Ergo, mir gefällt die statische Typprüfung in C++.
Deine beiden Hauptargumente gegen Signals/Slots sind Langsamkeit und nicht gegebene Typsicherheit. Gegen das erste läßt sich wenig ausrichten (das ist so vom Design her, asynchron ausgerichtet...), aber zumindest die Typsicherheit könnte man (auf Kosten der Compilezeit) erzwingen, indem z.B. eine all-am-local Regel ins Makefile.am eingefügt wird (oder was äquivalentes), die alle Sourcen, die sich verändert haben, nochmal durchparst. Den moc aufzubohren würde hier nichts bringen, aber den cpp verwenden oder kdoc aufbohren (was sowieso schon auf Signals/Slots angepasst ist und z.B. für die Java-Bindings-Generierung verwendet wird) sollte möglich sein. (jaja, die Konjunktive wieder...)
Ein aufgelöstes Makro von Konsole sieht etwa so aus:
connect( em, "2""notifySessionState(int)" , this, "1""notifySessionState(int)" );
2 Dinge wären nun zu überprüfen: - stimmen die Parameter überein? (einfach) - werden sie genau so vom Signal und Slot angeboten? (schwierig, weil über Klassen/Dateien hinweg geprüft werde müßte)
Aber ich selbst bin auch recht zufrieden mit dem jetzigen Konzept. Vor allem halte ich Qt zugute daß es relativ konsistent geblieben ist. 30'000 Zeilen Qt/KDE-basierten Code an zwei Tagen zu portieren (Qt 2.1/2.2 -> 3.0), und zusätzlich nochmal 70'000 Zeilen an einem Tag (threedom.sourceforge.net), das wäre mit anderen Toolkits sicher mächtig in die Hose gegangen. Ein wenig Bash/Sed war allerdings auch mit dabei ;)
Josef Spillner
lug-dd@mailman.schlittermann.de