Private slots


  • Qt Champions 2016

    Hi,
    I have been able to make use of "truly" private slots through the pointer to member syntax with lambdas. However, it starts to clutter unrelated parts of my code, which I would want to avoid. Consider for example this constructor:

    AedWoodsSaxonOperator::AedWoodsSaxonOperator(QObject * parent)
        : AedOperator(new AedWoodsSaxonOperatorPrivate(this), parent)
    {
        Q_D(AedWoodsSaxonOperator);
    
        auto onNucleusChange = [this, d] (AedNucleus * nucleus) -> void {
            QObject::disconnect(d->nucleonsChanged);
            d->nucleonsChanged = QObject::connect(nucleus, &AedNucleus::nucleonsChanged, [this, d] (quint16 nucleons) -> void {
                d->radius = 1.25 * std::cbrt(nucleons);
            });
        };
    
        QObject::connect(this, &AedOperator::modelChanged, this, [this, d, onNucleusChange] (AedModel * model) -> void {
            onNucleusChange(model->nucleus());
    
            QObject::disconnect(d->nucleusChanged);
            d->nucleusChanged = QObject::connect(model, &AedModel::nucleusChanged, this, onNucleusChange);
        });
    }
    

    It contains code that's completely unrelated to initialization and I'd love to move it out of there (ideally I'd place it in the private class). So what are my options? Am I stuck to Q_PRIVATE_SLOT and the old syntax?

    Thanks in advance.



  • Probably I got this question completely wrong but Q_DECLARE_PUBLIC(x) contains friend class x; so from the private part can't you access the private slot directly through the q pointer?


  • Qt Champions 2016

    I think you misunderstand me. I mean the following:
    I'd like to move this code into functions/slots inside the private class, not vice versa (it already is in the public class); the problem isn't accessing the public class from the private one. So currently I can attach lambdas but if I go with Q_PRIVATE_SLOT I'm stuck with the old connect syntax (as the private class isn't a QObject derived). Perhaps I'm missing something, hence the question ...



  • Ok, now I get it (or at least I think I do).

    you can use the method of the private class the same way you use a lambda (lambda is just a special case of the more general std::function call):

    testprivate.h

    #include <QObject>
    class TestCallSlotPrivate;
    class TestCallSlot : public QObject
    {
        Q_OBJECT
        Q_DECLARE_PRIVATE(TestCallSlot)
        Q_DISABLE_COPY(TestCallSlot)
    public:
        explicit TestCallSlot(QObject* parent = Q_NULLPTR);
        virtual ~TestCallSlot();
    protected:
        TestCallSlotPrivate* d_ptr;
    };
    

    testprivate.cpp

    #include "testprivate.h"
    #include <QTimer>
    #include <functional>
    class TestCallSlotPrivate{
        Q_DECLARE_PUBLIC(TestCallSlot)
        TestCallSlotPrivate(TestCallSlot* q)
            :q_ptr(q)
        {
            Q_ASSERT(q);
            QObject::connect(&m_privateTimer,&QTimer::timeout,q/*just gives context*/,std::bind(&TestCallSlotPrivate::privateSlot,this));
            m_privateTimer.start(300);
        }
    
        TestCallSlot* q_ptr;
        QTimer m_privateTimer;
        void privateSlot(){
            qDebug("Slot Called");
        }
    };
    
    
    
    TestCallSlot::TestCallSlot(QObject *parent)
        : QObject(parent)
        , d_ptr(new TestCallSlotPrivate(this))
    {
    }
    
    TestCallSlot::~TestCallSlot(){
        delete d_ptr;
    }
    


  • In your case:

    AedWoodsSaxonOperator::AedWoodsSaxonOperator(QObject * parent)
        : AedOperator(new AedWoodsSaxonOperatorPrivate(this), parent)
    {
        Q_D(AedWoodsSaxonOperator);
    
    
        QObject::connect(this, &AedOperator::modelChanged, this, std::bind(&AedWoodsSaxonOperatorPrivate::onModelChanged,d,std::placeholders::_1));
    }
    
    void AedWoodsSaxonOperatorPrivate::onNucleusChange(AedNucleus * nucleus){
    QObject::disconnect(nucleonsChanged);
    nucleonsChanged = QObject::connect(nucleus, &AedNucleus::nucleonsChanged, q_func(), std::bind(&AedWoodsSaxonOperatorPrivate::onNucleonsChanged,this,std::placeholders::_1));
    }
    
    void AedWoodsSaxonOperatorPrivate::onNucleonsChanged(quint16 nucleons){
    radius = 1.25 * std::cbrt(nucleons);
    }
    
    void AedWoodsSaxonOperatorPrivate::onModelChanged(AedModel * model){
    onNucleusChange(model->nucleus());
    QObject::disconnect(nucleusChanged);
    nucleusChanged = QObject::connect(model, &AedModel::nucleusChanged, q_func(), std::bind(&AedWoodsSaxonOperatorPrivate::onNucleusChange,this,std::placeholders::_1));
    }
    

  • Qt Champions 2016

    Well I am a dumb ass! This should work fine. Thanks, Luca!



  • @kshegunov said in Private slots:

    Well I am a dumb ass!

    You most definitely are not.

    Final question. Did you end up using std::bind or did you manage to find a way to use pre-C++11 function pointers in this case?


  • Qt Champions 2016

    @VRonin said in Private slots:

    Did you end up using std::bind or did you manage to find a way to use pre-C++11 function pointers in this case?

    Yes, I used std::bind. If I'm not mistaken it creates a wrapper call around the function pointer, right. So:

    std::bind(&AedWoodsSaxonOperatorPrivate::onModelChanged,d,std::placeholders::_1)
    

    Would expand to something like:

    void (AedWoodsSaxonOperatorPrivate::*ptm)(AedModel *) = &AedWoodsSaxonOperatorPrivate::onModelChanged;
    [d, ptm] (AedModel * model) -> void {
        (d->*ptm)(model);
    }
    

    Not that I can't do it manually, but I see no reason to bother with it. I use pre-C++11 pointer to member trickery in another place:

    class AedWoodsSaxonOperatorPrivate : public AedOperatorPrivate
    {
        Q_DECLARE_PUBLIC(AedWoodsSaxonOperator)
    
        typedef qreal (AedWoodsSaxonOperatorPrivate::*OneBodyMatrixElement)(qint32, qint32);
    
    public:
        // Definition is not provided and will never be. Just a forward declaration for the specializations.
        template <class Basis>
        qreal matrixElement(qint32, qint32);
    
    protected:
        OneBodyMatrixElement oneBodyMatrixElement;
    };
    

    Where that one is set like this:

    void AedWoodsSaxonOperatorPrivate::basisChanged(AedBasis * basis)
    {
        if (dynamic_cast<AedCylindricalBasis *>(basis))
            oneBodyMatrixElement = &AedWoodsSaxonOperatorPrivate::matrixElement<AedCylindricalBasis>;
        else
            oneBodyMatrixElement = nullptr;
    }
    

    This is relevant only for the private API (naturally) and the rationale is to forgo a second level of polymorphism and spare myself a virtual call, hopefully improving branch prediction. This one actually matters as it's called millions of times while for the one from the question above I don't care about any efficiency, it's called a handful of times.

    PS.
    If you're curious, current code can be found here


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.