0

Hi I'm a newbie in Qt and C++17 but I have to update a some private data from within a nested lambda function. These values are used to update the ui trough a timer:

This's the main window class:

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_connectButton_clicked();

protected:
    void timerEvent(QTimerEvent *event);

private:
    Ui::MainWindow *ui;
    mavsdk::Mavsdk mavsdk;
    std::shared_ptr<mavsdk::Telemetry> telemetry;
    std::shared_ptr<mavsdk::System> system;
    int timerId;
    double roll;
    double pitch;
    double yaw;
    double heading;
    double latitude;
    double longitude;
    double altitude;
    int messages;

    const QString mavURI = "udp://0.0.0.0:14550";
    const int telemetryFreq = 5;
    const int timerFreq = 33;

    void connectMAV();
};

And these are relevant methods:

void MainWindow::connectMAV()
{
    mavsdk::ConnectionResult conn_result = mavsdk.add_any_connection(mavURI.toStdString());

    setWindowTitle("Listening on: " + mavURI);

    mavsdk.subscribe_on_new_system
    (
        [&]()
        {
            setWindowTitle("Connected: " + mavURI);

            system = mavsdk.systems()[0];

            telemetry = std::shared_ptr<mavsdk::Telemetry>(new mavsdk::Telemetry(system));

            mavsdk::Telemetry::Result tel_action = mavsdk::Telemetry::Result::Unknown;

            do
            {
                tel_action = telemetry->set_rate_position(5);
                std::this_thread::sleep_for(std::chrono::seconds(telemetryFreq));
            } while(tel_action == mavsdk::Telemetry::Result::Timeout);

            do
            {
                tel_action = telemetry->set_rate_attitude_euler(telemetryFreq);
                std::this_thread::sleep_for(std::chrono::seconds(1));
            } while(tel_action == mavsdk::Telemetry::Result::Timeout);

            telemetry->subscribe_position([&] (mavsdk::Telemetry::Position position) {
                latitude = position.latitude_deg;
                longitude = position.longitude_deg;
                altitude = position.absolute_altitude_m;
            });

            telemetry->subscribe_attitude_euler([&] (mavsdk::Telemetry::EulerAngle attitude) {
                roll = attitude.roll_deg;
                pitch = attitude.pitch_deg;
                yaw = attitude.yaw_deg;
            });
        }
    );
}

void MainWindow::timerEvent(QTimerEvent *event){
    ui->eadi->setRoll(roll);
    ui->eadi->setPitch(pitch);
    ui->eadi->setAltitude(altitude);

    ui->eadi->redraw();
}

It seems that the main window is never updated. And debugging the code breakpoints inside inner lambdas are never triggered.

weirdgyn
  • 886
  • 16
  • 47

2 Answers2

1

You can create a private method that does exactly the same as the lambda, and call it using std::bind.

A simple example where we want to modify A::x:

#include <iostream>
#include <functional>

void call(std::function<void(int)>&& f, int x) {
    f(x);
}

class A {
private:
    int x{0};
    
    void set_x(int x_) { x = x_; }
    
public:
    void print() { std::cout << "x = " << x << std::endl; }
    
    void inc() {
        using namespace std::placeholders;

        call(std::bind(&A::set_x, this, _1), x+1);
    }
};

int main()
{
    A a;
    
    a.print();
    a.inc();
    a.inc();
    a.print();
}

Here call is the equivalent of your subscribe_on_new_system in the sense that it is receiving the function to be called.

You can test this example in Coliru.

cbuchart
  • 10,847
  • 9
  • 53
  • 93
0

I finally managed to fix this issue through Qt features. First of all I need to clarify that this problem needed a minimalistic solution since lambdas are the only way to manage callbacks for this specific API (mavsdk for instance). In short I cannot change the way the callbacks are invoked.

The above code should be modified in this way:

 mavsdk.subscribe_on_new_system
    (
        [&]()
        {
           emit newSystem();
        }
     );

of course somewhere I defined:

signals:
  void newSystem();

and:

private slots:
  void onNewSystem();

And connected it to the signal above the standard way:

connect(this, &MainWindow::newSystem, this, &MainWindow::onNewSystem)

The same schema apply also to nested lambdas.

So we got:

signals:
 void updatePosition(mavsdk::Telemetry::Position position);
 void updateAttitude(mavsdk::Telemetry::EulerAngle attitude);

private slots:
 void onPositionUpdate(mavsdk::Telemetry::Position position);
 void onAttitudeUpdate(mavsdk::Telemetry::EulerAngle attitude);

It was not so difficult after all..

weirdgyn
  • 886
  • 16
  • 47