Eine Frage des Stils



  • Hallo gemeinde,

    ich fange gerade an mit der GUI Programmierung an und stehe da vor einer Entscheidung die ich jetzt so noch nicht in der Gesamtheit überblicke. Deshalb frage ich jetzt hier einfach mal in die Runde ob es besser ich für das Verständniß und die Pflege beim Signal / Slot Konzept eher mit Makros zu arbeiten oder besser sich an Funktionszeiger zu gewöhnen.

    Für euere Anregungene bedanke ich mich schon jetzt im Voraus.

    Matze



  • Falls du hier von Qt sprichst:

    Nur bei Funktionen und Lambdas kann der Compiler auf Fehler prüfen. Eine Connection mit Makros dagegen tut einfach nichts, wenn das Programm läuft.



  • Ja ich spreche hier von Qt, bin da noch ziemlich unsicher wohin mich die Reise führt.



  • Qt hat soweit ich weiss einen eigenen Präprozessor der sich um Signals/Slots kümmert. Das hat mit Standard C++ nicht viel zu tun.

    Davon abgesehen sehe ich nicht wieso es eine Entscheidung Makros vs. Funktionszeiger sein muss. Es gibt in C++ ja schliesslich Lambdas , da muss man nicht mit Funktionszeigern rummachen wenn man nicht will.



  • @hustbaer sagte in Eine Frage des Stils:

    wieso es eine Entscheidung Makros vs. Funktionszeiger sein muss

    Ich denke es geht um

    connect(sender, &QObject::destroyed, this, &MyObject::objectDestroyed);
    

    vs

    connect(sender, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(Qbject*)));
    

    bzw. mit Lambda

    connect(sender, &QObject::destroyed, this, [=](){ this->m_objects.remove(sender); });
    

    (Beispiel aus https://doc.qt.io/qt-5/signalsandslots.html)



  • @manni66
    Meinst du das &QObject::destroyed im 3. Beispiel? Ist das denn ein Memberfunktionszeiger? Ich hätte eher erwartet dass es ein Data-Member-Pointer auf einen Funktor ist.



  • @hustbaer sagte in Eine Frage des Stils:

    @manni66
    Meinst du das &QObject::destroyed im 3. Beispiel? Ist das denn ein Memberfunktionszeiger? Ich hätte eher erwartet dass es ein Data-Member-Pointer auf einen Funktor ist.

    Links ist das Signal. Das kann man entweder als Funktionspointer oder per Makro SIGNAL im connect angeben. Es wird nicht selber implementiert sondern nur als Q_SIGNAL in der Klasse deklariert.

    Rechts ist der Slot. Den kann man entweder als Funktionspointer, per Makro SLOT oder als Lambda im connect angeben. Wird das Makro verwendet, muss die Funktion in der Klasse implementiert und als Slot markiert werden (z.B. mit Q_SLOT).



  • @manni66 sagte in Eine Frage des Stils:

    @hustbaer sagte in Eine Frage des Stils:

    @manni66
    Meinst du das &QObject::destroyed im 3. Beispiel? Ist das denn ein Memberfunktionszeiger? Ich hätte eher erwartet dass es ein Data-Member-Pointer auf einen Funktor ist.

    Links ist das Signal.

    Das ist mir schon klar.

    Das kann man entweder als Funktionspointer oder per Makro SIGNAL im connect angeben. Es wird nicht selber implementiert sondern nur als Q_SIGNAL in der Klasse deklariert.

    Dass man es nicht selbst implementiert ist mir auch klar. Was ich nicht weiss ist was der Qt-Präprozessor da draus macht. Also ob das wirklich eine Memberfunktion ist. Wenn das nämlich eine Memberfunktion ist, dann müsste man um an die Subscriber zu kommen einen Map-Lookup mit einem Memberfunktionszeiger als Key machen. Was ich schräg fände. Andrerseits ... Qt muss man das wohl zutrauen.



  • s. Qt Signals & Slots:

    Signals

    Signals are emitted by an object when its internal state has changed in some way that might be interesting to the object's client or owner. Signals are public access functions and can be emitted from anywhere, but we recommend to only emit them from the class that defines the signal and its subclasses.

    Slots

    A slot is called when a signal connected to it is emitted. Slots are normal C++ functions and can be called normally; their only special feature is that signals can be connected to them.

    Since slots are normal member functions, they follow the normal C++ rules when called directly.

    Ja, es sind also beides (normale) Memberfunktionen.

    Edit:
    Und hier die technische Erklärung zur Implementation: How Qt Signals and Slots Work - Part 2 - Qt5 New Syntax



  • https://wiki.qt.io/New_Signal_Slot_Syntax
    https://doc.qt.io/qt-5/signalsandslots-syntaxes.html

    Sprich: Neue Syntax, wenn es geht ... alte Syntax, wenn es sein muss 😉



  • @Th69
    Danke. Der Code sieht ja einigermassen Brutal aus. Type punning mit zwei Member-Function Pointer Typen. Ziemlich sicher ne Strict-Aliasing Verletzung. Brrr.



  • @hustbaer seh' ich nicht?



  • @Swordfish

    template <typename Func1, typename Func2>
    static inline QMetaObject::Connection connect(
        const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
        const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,
        Qt::ConnectionType type = Qt::AutoConnection)
    {
    // Hier kommt der echte mfn-pointer rein (slot)
    // ...
    
    // Hier wird ein Zeiger auf den mfn-pointer übergeben (als void**, was noch OK ist)
      return connectImpl(sender, reinterpret_cast<void **>(&signal),
                         receiver, reinterpret_cast<void **>(&slot), slotObj,
                         type, types, &SignalType::Object::staticMetaObject);
    }
    
    // Den Teil dazwischen sehen wir leider nicht
    
    void Counter::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
    {
    ...
        } else if (_c == QMetaObject::IndexOfMethod) {
            int *result = reinterpret_cast<int *>(_a[0]);
            void **func = reinterpret_cast<void **>(_a[1]); // Zeiger auf den mfn-pointer holen
            {
                typedef void (Counter::*_t)(int ); // Dummy mfn-pointer-typ
                // Hier wird etwas als Dummy mfn-pointer-typ gelesen -- ich vermute mal dass das 1:1 der "slot" Zeiger aus der "connect" Funktion ist
                // -> type punning zwischen zwei mfn-pointer Typen
                if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Counter::valueChanged)) {
                    *result = 0;
                }
            }
    ...
    }
    

    Der Code zwischen connect und qt_static_metacall wird leider nicht gezeigt. Allerdings sieht man dass die connectImpl Funktion den echten Typ des mfn-pointers nicht kennen kann, da sie ihn aus keinem der Parameter herleiten kann. Also falls ich da jetzt nichts übersehen habe. Passt auf jeden Fall dazu wie es dann in qt_static_metacall weiter geht um den Index zu ermitteln. Brrr.



  • @hustbaer Danke. Durch die vielen Links war mir garnicht klar welchen Code man ansehen soll.


Anmelden zum Antworten