TSB99X
TSB99X

Reputation: 3394

C++11. Lambdas member variable capture, "this" pointer pitfall in STL list

I have a simple eventing system with simple button. This system is driven by std::function list with assigned lambdas inside.

Here is full button class:

class Button {

private:

    Square square;
    Text label;
    bool hovered = false;
    std::function <void ()> on_mouse_enter;
    std::function <void ()> on_mouse_leave;

public:

    Button (const Square& SQUARE, const Text& LABEL):
        square {SQUARE},
        label {LABEL}
    {
        on_mouse_enter = [this] () {
            square.set_color(1, 1, 1);
        };

        on_mouse_leave = [this] () {
            square.set_color(0, 0, 0);
        };
    }

    std::function <void (const Render&)> get_rendering() {
        return [this] (const Render& RENDER) {
            RENDER.draw(square);
            RENDER.draw(label);
        };
    }

    std::function <void (const Point&)> get_updating() {
        return [this] (const Point& CURSOR) {
            if (not hovered) {
                if (is_including(square, CURSOR)) {
                   hovered = true;
                   if (on_mouse_enter)
                       on_mouse_enter();
                }
            } else
                if (not is_including(square, CURSOR)) {
                   hovered = false;
                   if (on_mouse_leave)
                       on_mouse_leave();
                }
        };
    }

};

And i add such button to event manager, like this:

Button button {/*SOME_PARAMS_HERE*/};

mngr.push_to_render(button.get_render());
mngr.push_to_updater(button.get_updater());

It works perfect, no problems, on_mouse_enter and on_mouse_leave works as supposed.

But if I do something with STL container wrapper, like this:

std::list <Button> sb;

sb.emplace_back(Button {/*SOME_PARAMS_HERE*/});

mngr.push_to_render(sb.back().get_render());
mngr.push_to_updater(sb.back().get_updater());

Whole thing is falling apart. on_mouse_enter and on_mouse_leave is not working as supposed to.

With outputting debug messages, I can see that square, accessed by this in on_mouse_enter and on_mouse_leave is not squares they supposed to be, next I see that this is not what it supposed to be.

What is wrong with this kind of captures and how it could be solved?

Upvotes: 3

Views: 715

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275330

Don't capture this if you are going to be copied. Whatever you capture, you are in charge of managing lifetime for.

Second, a pointer to the button being passed to the enter/leave makes lots of sense.

std::function<void(Button*)> on_mouse_enter;
std::function<void(Button*)> on_mouse_leave;

Then we have:

    on_mouse_enter = [] (Button* but) {
        but->square.set_color(1, 1, 1);
    };

    on_mouse_leave = [] (Button* but) {
        but->square.set_color(0, 0, 0);
    };

and the copy constructor no longer leaves you with pointers to a different this.

Finally, when you call on_mouse_enter, pass this.

Upvotes: 5

Related Questions