Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read Access Violation after Lambda-Callback

Tags:

c++

lambda

For a small GUI I first wanted to use function pointers; after reading lots of stuff I find that Lambda Callback Functions might be a better solution for this (?).

I always get a read access violation and guess I'm missing some important knowledge. It makes no difference if I use the copy or reference constructor in the lambda-call - I still get the exception.

#include <iostream>
#include <functional>

using namespace std;

//=======================================================

class Button {
private:
    function<void()> const &callback;

public:
    Button(function<void()> const &c) : callback(c) {
        cout << "created Button" << endl;
        callback();     // THIS CALL WORKS FINE
    }

    void update() {
        // if(clicked)...
        callback();     // HERE I GET THE READ ACCESS VIOLATION
    }
};

//=======================================================

class VideoPreview {
private:
    Button *btn;

public:
    VideoPreview() {
        cout << "created VideoPreview" << endl;
        btn = new Button([=]() {
            //cout << "you clicked the button" << endl;
            buttonClicked();
        });
    }

    void update() { // "check the gui"
        btn->update();
    }

    void buttonClicked() {
        cout << "you clicked the button" << endl;
    }

};

//=======================================================

void main() {
    VideoPreview foo;
    foo.update();
}
like image 619
spacetimedivider Avatar asked Jan 28 '19 15:01

spacetimedivider


1 Answers

The problem here is that function<void()> const &callback; doesn't extend the life of the temporary std::function that is generated from

btn = new Button([=]() {
    //cout << "you clicked the button" << endl;
    buttonClicked();
});

A lambda is not a std::function so when you do the above the temporary std::function is generated. You then bind to that temporary with callback but const& member variables do not extend the lifetime of the temporary they bind to. Only function local const& get that behavior. That means when you exit VideoPreview's constructor you are left with a button that has a function reference to a destroyed object.

To fix this just store callback by value like

class Button {
private:
    function<void()> callback;

public:
    Button(function<void()> c) : callback(std::move(c)) {
        cout << "created Button" << endl;
        callback();     // THIS CALL WORKS FINE
    }

    void update() {
        // if(clicked)...
        callback();     // works now.
    }
};
like image 67
NathanOliver Avatar answered Nov 01 '22 09:11

NathanOliver