Verwenden von emit vs Aufrufen eines Signals, als wäre es eine reguläre Funktion in Qt

97

Angenommen, ich habe dieses Signal:

signals:
    void progressNotification(int progress);

Ich habe erst kürzlich etwas über das Schlüsselwort emit in Qt erfahren. Bisher habe ich Signale ausgeführt, indem ich sie wie eine reguläre Funktion aufgerufen habe. Also statt:

emit progressNotification(1000 * seconds);

Ich würde schreiben:

progressNotification(1000 * seconds);

Es schien zu funktionieren, sie so aufzurufen, und alle verbundenen Slots würden ausgeführt. Verursacht die Verwendung des Schlüsselworts emit ein anderes Verhalten, oder handelt es sich nur um syntaktischen Zucker?

Sashoalm
quelle
17
+1 Nie gewusst emitwird nicht benötigt. Es ist jedoch seltsam, dass Sie emitlange nach dem direkten Aufrufen von Signalen davon erfahren haben , da das Signal-Slot-System eines der ersten Dinge ist, die Sie über Qt lernen sollten.
Christian Rau

Antworten:

88

emitist nur syntaktischer Zucker. Wenn Sie sich die vorverarbeitete Ausgabe der Funktion ansehen, die ein Signal aussendet, sehen Sie, dass sie emiteinfach weg ist.

Die "Magie" geschieht im generierten Code für die Signalemissionsfunktion, die Sie durch Überprüfen des von moc generierten C ++ - Codes betrachten können.

Zum Beispiel erzeugt ein fooSignal ohne Parameter diese Elementfunktion:

void W::foo()
{
    QMetaObject::activate(this, &staticMetaObject, 0, 0);
}

Und der Code emit foo();wird einfach vorverarbeitetfoo();

emitwird wie folgt definiert Qt/qobjectdefs.h(im Open-Source-Flair der Quelle):

#ifndef QT_NO_EMIT
# define emit
#endif

(Mit dem Define Guard können Sie Qt mit anderen Frameworks verwenden, deren Namen über die no_keywordsQMake-Konfigurationsoption kollidieren .)

Matte
quelle
14
Wissen Sie, ob es jemals eine Implementierung (oder eine geplante Implementierung) von einer gab emit, die tatsächlich mehr als nichts getan hat? Ich finde, dass der 'syntaktische Zucker' in diesem Fall nur den Anfänger (oder zumindest mich, als ich ein unerfahrener Qt-Benutzer war) verwirrt - es scheint, dass mit dem emitPseudo-Schlüsselwort etwas Magisches oder Wichtiges passiert , wenn es nichts tut all - all die Magie geschieht in einer regulären alten Funktion, die mocerzeugt ( mocist die Magie für Qt-Signale und -Slots). emitist unnötige Dekoration, die nichts anderes tut, als wichtig zu sein.
Michael Burr
12
Emission ist nicht "nur Dekoration". emitteilt der Person, die den Anruf liest, mit, dass Magie bevorsteht (dh dies löst Code in Objekten aus, von denen diese Klasse möglicherweise noch nie gehört hat, und diese Aufrufe können synchron oder asynchron sein), was im Wesentlichen vollständig verloren geht, wenn Sie das Schlüsselwort weglassen. Benutze es. Es wird automatisch dokumentiert. "Anfänger" sollten Dokumente und Tutorials lesen und emitsind immer da (in den offiziellen Dokumenten sowieso). Die Entdeckung, dass Sie die Funktion einfach aufrufen können, sollte erfolgen, nachdem Sie "das Licht gesehen" haben - zu diesem Zeitpunkt sind Sie kein Anfänger mehr.
Mat
19
Hmm, ich bin mir nicht sicher, ob ich Ihnen zustimme, wie wertvoll das emit"Schlüsselwort" ist. Ich denke, ich hätte es vorgezogen, eine Namenskonvention zu verwenden, wenn klargestellt werden müsste, dass ein Funktionsaufruf ein Signal ist.
Michael Burr
2
Nun, ich bin damit radikal nicht einverstanden :) Das Erzwingen einer Namenskonvention ist etwas, das Sie selbst in Ihren Projekten / am Arbeitsplatz tun können. Qt verhindert das nicht. Qt zwingt Sie nicht dazu, das "Schlüsselwort" zu verwenden, und ermöglicht es Ihnen sogar, es zu deaktivieren, wenn es mit anderen Teilen Ihres Codes kollidiert. Meiner Meinung nach ist der Keyword-Ansatz besser - der Compiler kann Ihnen nicht dabei helfen, Namensrichtlinien durchzusetzen, aber er wird einen Rechtschreibfehler erkennen emit.
Mat
15
Um klar zu sein - ich habe nicht befürwortet, dass eine Namenskonvention verwendet wird - nur, dass, wenn der Grund für einen emitPseudo-Schlüsselwort-Kommentar darin bestand, klar zu machen, dass ein Signal aufgerufen wird, eine Namenskonvention dasselbe tun könnte, ohne Geheimnis und mit ähnlichen Vorteilen. Die Namenskonvention konnte von Qt nicht durchgesetzt werden ( mockönnte sie tatsächlich durchsetzen - aber ich befürworte das auch nicht), aber Qt kann die Verwendung von emitbeiden nicht durchsetzen . Und während Sie emitbei einem Namenskonflikt "ausschalten" können, hilft dies nicht viel, wenn Sie eine Reihe von Quelldateien haben, die es verwenden (unnötig, um zu booten).
Michael Burr
2

Nach 18 Monaten ... begann ich mit Kommentaren unter @ Mats Antwort und hatte schnell keinen Platz mehr. Also die Antwort.

IMO emitist weder syntaktischer Zucker noch ein einfaches Schlüsselwort in dem Sinne, dass

  1. Es generiert Code (wie oben von @Mat erläutert).
  2. Es hilft dem connectMechanismus zu erkennen, dass es sich tatsächlich um ein signalund handelt
  3. Es macht Ihr Signal zu einem Teil eines "größeren" Systems, in dem Signale und Antworten (Slots) synchron oder asynchron ausgeführt oder in eine Warteschlange gestellt werden können, je nachdem, wo und wie das Signal gesendet wurde. Dies ist eine äußerst nützliche Funktion des Signal- / Steckplatzsystems.

Das gesamte Signal- / Slot-System ist eine andere Sprache als ein einfacher Funktionsaufruf. Ich glaube, es ergibt sich aus dem Beobachtermuster. Es gibt auch einen großen Unterschied zwischen a signalund a slot: Ein Signal muss nicht implementiert werden, während ein Slot sein muss !

Sie gehen die Straße entlang und sehen ein brennendes Haus (ein Signal). Sie wählen 911 ( verbinden Sie das Feuersignal mit dem 911-Antwortsteckplatz ). Das Signal wurde nur ausgesendet , während der Slot von der Feuerwehr implementiert wurde. Mag ungenau sein, aber Sie bekommen die Idee. Schauen wir uns das Beispiel von OP an.

Einige Backend-Objekte wissen, wie viel Fortschritt erzielt wurde. Es könnte also einfach emit progressNotification(...)signalisieren. Es liegt an der Klasse, die den tatsächlichen Fortschrittsbalken anzeigt, dieses Signal aufzunehmen und darauf auszuführen. Aber wie verbindet sich die Ansicht mit diesem Signal? Willkommen beim Signal- / Slot-System von Qt. Man kann sich jetzt eine Manager-Klasse vorstellen (typischerweise eine Art Widget), die aus einem Ansichtsobjekt und einem Datenberechnungsobjekt (beide sind QObjects) besteht connect (m_myDataEngine, &DataEngine::progressNotification, m_myViewObj, &SimpleView::displayProgress).

Lassen Sie uns nicht auf die Designaspekte der Manager-Klasse eingehen, aber es genügt zu sagen, dass hier das Signal- / Slot-System glänzt. Ich kann mich darauf konzentrieren, eine sehr saubere Architektur für meine Anwendung zu entwerfen. Nicht immer, aber oft stelle ich fest, dass ich nur Signale aussende, aber Slots implementiere .

Wenn es möglich ist, eine Signalmethode zu verwenden / aufzurufen, ohne sie jemals auszusenden , bedeutet dies zwangsläufig, dass Sie diese Funktion überhaupt nicht als Signal benötigt haben.

NameRakes
quelle
6
Nein, emitist in der Tat nur ein leeres Makro und rein optional. Nicht so sind die Schlüsselwörter signalund slotdie durch die moc verarbeitet werden. signalwird verwendet, um die Implementierung der Funktion bereitzustellen, slotwird verwendet, um den Metaobjekteintrag so zu erstellen, dass er mit dem SLOT(MySlot())Makro oder in QML gefunden wird. emitist syntaktischer Zucker. Nichts wird sich jemals beschweren, wenn Sie schreiben emit i++;(aber vielleicht Ihre Kollegen) und Sie immer noch keine Verbindung herstellen können i++.
derM
-5

Die zweite Option würde bedeuten, dass Sie immer wissen, wie der Funktionsname und die Funktionsparameter lauten und dass das Objekt, an das Sie es senden, dieser bestimmten Funktion bekannt ist. Diese beiden Fälle sind nicht immer wahr, daher sind dies die beiden Hauptgründe, warum Slots und Signale erstellt wurden. "unter der Haube" Der Signal- und Slot-Mechanismus ist nur eine Tabelle mit Zeigern auf jede angeschlossene Funktion.

Schauen Sie sich auch dieses PDF an, das die Art des Signal- und Slot-Mechanismus sehr deutlich erklärt: http://www.elpauer.org/stuff/a_deeper_look_at_signals_and_slots.pdf

Evert
quelle
In beiden Fällen müssen Sie den Signalnamen und seine Parameter kennen - Sie senden ihn aus. Wie können Sie etwas ausgeben, das Sie nicht kennen? Beide haben auch die gleiche Semantik, sie sind identisch.
Mat
1
Vielleicht vermasseln Sie einen Signalanruf mit einem direkten Slot-Anruf? Aber ich muss zugeben, dass ich mich zuerst auch über den Fragentitel gewundert habe, da ich nie wusste, dass emites nur ein No-Op ist. Aber auch in diesem Fall hätte das Lesen des Fragenkörpers die Dinge klären müssen, also -1.
Christian Rau