Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to pass and invoke lambda function at slot ?
Forum Updated to NodeBB v4.3 + New Features

How to pass and invoke lambda function at slot ?

Scheduled Pinned Locked Moved Solved General and Desktop
12 Posts 3 Posters 2.9k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • H Offline
    H Offline
    huseyinkozan
    wrote on last edited by huseyinkozan
    #1

    Hi,

    I want to take lambda as function parameter and invoke it, but couldn't realize how to do that. For ex:

    MyObject::ask(const QString & text, QObject * receiver, / * param type ? */ func) {
        // setup m_msgBox to ask question
        connect(m_msgBox,  &QMessageBox::finished, this, [&](int result) {
            func(result); // how to call lambda function ?
        });
        m_msgBox->open();
    }
    
    MyObject::someFunction() {
        ask(tr("How?"), this, [&](int result) {
            // process result
        });
    }
    
    1 Reply Last reply
    0
    • fcarneyF Offline
      fcarneyF Offline
      fcarney
      wrote on last edited by
      #2

      std::function

      MyObject::ask(const QString & text, QObject * receiver, std::function<void(int)> func)
      

      or

      template<class T>
      MyObject::ask(const QString & text, QObject * receiver, T func)
      

      The template one can get messy for class members, but is still doable.

      C++ is a perfectly valid school of magic.

      H 1 Reply Last reply
      0
      • fcarneyF fcarney

        std::function

        MyObject::ask(const QString & text, QObject * receiver, std::function<void(int)> func)
        

        or

        template<class T>
        MyObject::ask(const QString & text, QObject * receiver, T func)
        

        The template one can get messy for class members, but is still doable.

        H Offline
        H Offline
        huseyinkozan
        wrote on last edited by huseyinkozan
        #3

        @fcarney Thanks.

        I tried to did first way, and call like:

        MyObject::ask(const QString & text, QObject * receiver, std::function<void(int)> func) {
        ...
        func(result);
        

        But failed at functional header:

        __throw_bad_function_call();
        

        Also, should I use receiver ?

        1 Reply Last reply
        0
        • fcarneyF Offline
          fcarneyF Offline
          fcarney
          wrote on last edited by
          #4

          Why are you passing this here?:

          connect(m_msgBox,  &QMessageBox::finished, this, [&](int result) {
          

          Can you create a minimal project that shows the error? Something we can compile?

          C++ is a perfectly valid school of magic.

          H 1 Reply Last reply
          0
          • fcarneyF fcarney

            Why are you passing this here?:

            connect(m_msgBox,  &QMessageBox::finished, this, [&](int result) {
            

            Can you create a minimal project that shows the error? Something we can compile?

            H Offline
            H Offline
            huseyinkozan
            wrote on last edited by
            #5

            @fcarney Right, no need to pass this.
            Found the problem, changing the capture from & to = solved the exception.

            #include <QApplication>
            #include <QMessageBox>
            #include <QPushButton>
            #include <QDebug>
            #include <QScreen>
            #include <QStyle>
            
            class Button : public QPushButton
            {
                Q_OBJECT
            public:
                Button(QWidget *parent = nullptr) : QPushButton(parent) {
                    setText(tr("Click"));
                    m_msgBox = new QMessageBox(this);
                    connect(this, &QPushButton::clicked, [&](){
                        askProceed();
                    });
                }
                virtual ~Button() {}
            
                void ask(
                        const QString & text,
                        const QStringList & buttons,
                        int acceptButton,
                        int rejectButton,
                        std::function<void(int)> func)
                {
                    m_msgBox->setText(text);
                    m_msgBox->setIcon(QMessageBox::Question);
                    m_msgBox->setStandardButtons(QMessageBox::NoButton);
                    QList<QAbstractButton*> buttonList;
                    for (int i = 0; i < buttons.count(); ++i) {
                        if (i == acceptButton) {
                            QPushButton * btn = m_msgBox->addButton(buttons.at(i), QMessageBox::AcceptRole);
                            m_msgBox->setDefaultButton(btn);
                            buttonList.append((QAbstractButton*)btn);
                        }
                        else if (i == rejectButton) {
                            QPushButton * btn = m_msgBox->addButton(buttons.at(i), QMessageBox::RejectRole);
                            m_msgBox->setEscapeButton((QAbstractButton*)btn);
                            buttonList.append((QAbstractButton*)btn);
                        }
                        else {
                            Q_ASSERT(false);
                        }
                    }
                    connect(m_msgBox,  &QMessageBox::finished, [=](int) {
                        int res = rejectButton;
                        for (int i = 0; i < buttonList.count(); ++i) {
                            if (m_msgBox->clickedButton() == buttonList.at(i)) {
                                res = i;
                                break;
                            }
                        }
                        QList<QAbstractButton*> listToDelete = m_msgBox->buttons();
                        foreach (QAbstractButton * btn, listToDelete) {
                            m_msgBox->removeButton(btn);
                            btn->deleteLater();
                        }
                        m_msgBox->disconnect();
                        func(res);
                    });
                    m_msgBox->open();
                }
                void askProceed() {
                    ask(
                                tr("Which do you prefer?"),
                                QStringList() << tr("Apple") << tr("Pineapple"),
                                0, 1, [&](int result) {
                        if (result == 0)
                            qDebug() << "Selected Apple";
                        else if (result == 1)
                            qDebug() << "Selected Pineapple";
                        else
                            qDebug() << "Unexpected result = " << result;
            
                    });
                }
            private:
                QMessageBox * m_msgBox = nullptr;
            };
            
            int main(int argc, char *argv[])
            {
                QApplication a(argc, argv);
                Button btn;
                btn.resize(100, 30);
                btn.setGeometry(
                    QStyle::alignedRect(
                        Qt::LeftToRight,
                        Qt::AlignCenter,
                        btn.size(),
                        qApp->screens().first()->availableGeometry()
                    )
                );
                btn.show();
                return a.exec();
            }
            
            #include "main.moc"
            

            Also added to my snippets:
            https://gitlab.com/snippets/1888176

            1 Reply Last reply
            1
            • H Offline
              H Offline
              huseyinkozan
              wrote on last edited by
              #6

              @fcarney said in How to pass and invoke lambda function at slot ?:

              Why are you passing this here?:
              connect(m_msgBox, &QMessageBox::finished, this, [&](int result) {

              Should we check if the object and function exists before calling the callback ?

              KroMignonK 1 Reply Last reply
              0
              • H huseyinkozan

                @fcarney said in How to pass and invoke lambda function at slot ?:

                Why are you passing this here?:
                connect(m_msgBox, &QMessageBox::finished, this, [&](int result) {

                Should we check if the object and function exists before calling the callback ?

                KroMignonK Offline
                KroMignonK Offline
                KroMignon
                wrote on last edited by
                #7

                @huseyinkozan said in How to pass and invoke lambda function at slot ?:

                Should we check if the object and function exists before calling the callback ?

                When a QObject is deleted, all connection on this object are removed ==> Take a look at QObject::~QObject() for more details

                It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                H 1 Reply Last reply
                0
                • KroMignonK KroMignon

                  @huseyinkozan said in How to pass and invoke lambda function at slot ?:

                  Should we check if the object and function exists before calling the callback ?

                  When a QObject is deleted, all connection on this object are removed ==> Take a look at QObject::~QObject() for more details

                  H Offline
                  H Offline
                  huseyinkozan
                  wrote on last edited by
                  #8

                  @kromignon Thanks.
                  Then we can be sure that all childs of deleted QObject will disconnect.

                  At my example, m_msgBox will be deleted after btn delete, and lambda for &QMessageBox::finished will not call. I prefer checking func is not a dangling pointer. But seems not possible with std::function::operator==.

                  I tried to find at QObject::connect() code at woboq, but couldn't find how to handle lambdas.

                  KroMignonK 1 Reply Last reply
                  0
                  • H huseyinkozan

                    @kromignon Thanks.
                    Then we can be sure that all childs of deleted QObject will disconnect.

                    At my example, m_msgBox will be deleted after btn delete, and lambda for &QMessageBox::finished will not call. I prefer checking func is not a dangling pointer. But seems not possible with std::function::operator==.

                    I tried to find at QObject::connect() code at woboq, but couldn't find how to handle lambdas.

                    KroMignonK Offline
                    KroMignonK Offline
                    KroMignon
                    wrote on last edited by
                    #9

                    @huseyinkozan said in How to pass and invoke lambda function at slot ?:

                    but couldn't find how to handle lambdas.

                    Qt documentation is big, so find the right information is not that easy, but Google is very performant for "data mining".
                    A simple search with "connect functor" and you will find Making Connections to Lambda Expressions

                    It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                    H 1 Reply Last reply
                    0
                    • KroMignonK KroMignon

                      @huseyinkozan said in How to pass and invoke lambda function at slot ?:

                      but couldn't find how to handle lambdas.

                      Qt documentation is big, so find the right information is not that easy, but Google is very performant for "data mining".
                      A simple search with "connect functor" and you will find Making Connections to Lambda Expressions

                      H Offline
                      H Offline
                      huseyinkozan
                      wrote on last edited by huseyinkozan
                      #10

                      @kromignon Thanks.
                      I had found the page that you gave, but couldn't realise how QObject::connect() accepts lambdas.

                      I am using lambdas newly. Just read this article, and helped alot:
                      https://blog.feabhas.com/2014/03/demystifying-c-lambdas/

                      1 Reply Last reply
                      0
                      • KroMignonK Offline
                        KroMignonK Offline
                        KroMignon
                        wrote on last edited by
                        #11

                        @huseyinkozan Thanks for the link, I've found this one https://medium.com/genymobile/how-c-lambda-expressions-can-improve-your-qt-code-8cd524f4ed9f

                        It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                        H 1 Reply Last reply
                        0
                        • KroMignonK KroMignon

                          @huseyinkozan Thanks for the link, I've found this one https://medium.com/genymobile/how-c-lambda-expressions-can-improve-your-qt-code-8cd524f4ed9f

                          H Offline
                          H Offline
                          huseyinkozan
                          wrote on last edited by huseyinkozan
                          #12

                          @kromignon Thanks.

                          I had also read that article, but that also not defines how Qt handle this stuation:

                          In this version, we pass monitor as a context to connect(). It won't affect the execution of our lambda, but when monitor is deleted, Qt will notice and will disconnect Worker::progress() from our lambda.

                          I tried with below, but could not find how to detect I have already disconnect:

                          connect(receiver, &QObject::destroyed, [this](){
                              qDebug() << "receiver destroyed";
                              m_msgBox->disconnect();
                          });
                          

                          Full code with receiver (deletes btn after 10 sec!):

                          #include <QApplication>
                          #include <QMessageBox>
                          #include <QPushButton>
                          #include <QDebug>
                          #include <QScreen>
                          #include <QStyle>
                          #include <QTimer>
                          
                          class Button : public QPushButton
                          {
                              Q_OBJECT
                          public:
                              Button(QWidget *parent = nullptr) : QPushButton(parent) {
                                  setText(tr("Click"));
                                  m_msgBox = new QMessageBox(this);
                                  connect(this, &QPushButton::clicked, [this](){
                                      ask(tr("Which do you prefer?"),
                                          QStringList() << tr("Apple") << tr("Pineapple"),
                                          0, 1, this,
                                          [](int result) {
                                            if (result == 0)
                                                qDebug() << "Selected Apple";
                                            else if (result == 1)
                                                qDebug() << "Selected Pineapple";
                                            else
                                                qDebug() << "Unexpected result = " << result;
                                      });
                                  });
                              }
                              virtual ~Button() {}
                          
                              void ask(
                                      const QString & text,
                                      const QStringList & buttons,
                                      int acceptButton,
                                      int rejectButton,
                                      const QObject * receiver,
                                      std::function<void(int)> func)
                              {
                                  m_msgBox->setText(text);
                                  m_msgBox->setIcon(QMessageBox::Question);
                                  m_msgBox->setStandardButtons(QMessageBox::NoButton);
                                  QList<QAbstractButton*> buttonList;
                                  for (int i = 0; i < buttons.count(); ++i) {
                                      if (i == acceptButton) {
                                          QPushButton * btn = m_msgBox->addButton(buttons.at(i), QMessageBox::AcceptRole);
                                          m_msgBox->setDefaultButton(btn);
                                          buttonList.append((QAbstractButton*)btn);
                                      }
                                      else if (i == rejectButton) {
                                          QPushButton * btn = m_msgBox->addButton(buttons.at(i), QMessageBox::RejectRole);
                                          m_msgBox->setEscapeButton((QAbstractButton*)btn);
                                          buttonList.append((QAbstractButton*)btn);
                                      }
                                      else {
                                          Q_ASSERT(false);
                                      }
                                  }
                                  connect(m_msgBox,  &QMessageBox::finished, [this, rejectButton, buttonList, func](int) {
                                      m_msgBox->disconnect();
                                      int res = rejectButton;
                                      for (int i = 0; i < buttonList.count(); ++i) {
                                          if (m_msgBox->clickedButton() == buttonList.at(i)) {
                                              res = i;
                                              break;
                                          }
                                      }
                                      QList<QAbstractButton*> listToDelete = m_msgBox->buttons();
                                      foreach (QAbstractButton * btn, listToDelete) {
                                          m_msgBox->removeButton(btn);
                                          btn->deleteLater();
                                      }
                                      func(res);
                                  });
                                  connect(receiver, &QObject::destroyed, [this](){
                                      qDebug() << "receiver destroyed";
                                      m_msgBox->disconnect();
                                  });
                                  m_msgBox->open();
                              }
                          private:
                              QMessageBox * m_msgBox = nullptr;
                          };
                          
                          int main(int argc, char *argv[])
                          {
                              QApplication a(argc, argv);
                              Button * btn = new Button;
                              btn->resize(100, 30);
                              btn->setGeometry(
                                  QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, btn->size(),
                                                      qApp->screens().first()->availableGeometry()));
                              btn->show();
                              QTimer::singleShot(10000, [&](){
                                  delete btn;
                                  btn = nullptr;
                              });
                              int res = a.exec();
                              if (btn)
                                  delete btn;
                              return res;
                          }
                          
                          #include "main.moc"
                          
                          1 Reply Last reply
                          0

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved