Reputation: 3394
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
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