Lambda Evaluation in Signal/Slot Connection or QTimer
-
Hi I have a question about lambda evaluation in signal/slot connections or QTimer::singleShots. Sometimes you want to pass a variable allocated on the stack to a lambda and obviously you need the value to be passed at creation of the signal/slot connection or QTimer::singleShot or else the variable might not have a value any more and get overwritten with other data when the lambda is executed. Other times you want a variable to have it's value evaluated when the lambda is executed, say a boolean flag that could change from connection/timer creation to when the lambda is executed. My question is are signal/slot connections and QTimer:singleShots smart enough to tell when you want one behavior or the other? Do they always resort to one behavior and not the other depending on the capture type?
Thanks!//Out of Scope Example void Class::function() { QString inputString; //user stores characters in string then timer prints string after string falls out of scope QTimer::singleShot(2000,[=]() { QDebug() << inputString; }); } //In Scope Example bool jobDone is member of class void Class::function() { Connect(this,Class::closeWindow, [&]() { if (jobDone) window.close(); }); }
-
Thanks guys appreciate the help. To clarify I was actually wondering am I correct about this: The lambdas capture the variables immediately upon creation and then are executed using those captures at the timeout of the timer or execution of the slot.
@Crag_Hack
Yes, as I wrote earlier ISTM you do understand lambdas and captures! :) Lambdas do their capture work where they are in the code, e.g. as aconnect()
statement is met to set up a signal/slot. The lambda-slot then uses those variables in its code when the slot is executed at a future time. With an=
the variable's value is copied atconnect()
-time, so the slot will always its value as it was at that instant, while with an&
only a reference to the variable, not its current value, is copied, so when accessed in the slot it will have its value as it is at that time. An&
-captured variable must therefore still be in scope/alive when the lambda body executes, which is not required/not relevant for an=
-captured one. -
Hi I have a question about lambda evaluation in signal/slot connections or QTimer::singleShots. Sometimes you want to pass a variable allocated on the stack to a lambda and obviously you need the value to be passed at creation of the signal/slot connection or QTimer::singleShot or else the variable might not have a value any more and get overwritten with other data when the lambda is executed. Other times you want a variable to have it's value evaluated when the lambda is executed, say a boolean flag that could change from connection/timer creation to when the lambda is executed. My question is are signal/slot connections and QTimer:singleShots smart enough to tell when you want one behavior or the other? Do they always resort to one behavior and not the other depending on the capture type?
Thanks!//Out of Scope Example void Class::function() { QString inputString; //user stores characters in string then timer prints string after string falls out of scope QTimer::singleShot(2000,[=]() { QDebug() << inputString; }); } //In Scope Example bool jobDone is member of class void Class::function() { Connect(this,Class::closeWindow, [&]() { if (jobDone) window.close(); }); }
For the first one I'm not 100% sure, but the second one uses the value
jobDone
has, when the signal is emitted.
As long asjobDone
is a member, it's ok to use... using a local variable there will even throw a warning
like
captured local variable by reference might go out of scope before lambda is called [clazy-lambda-in-connect]
Edit: If I'm not mistaken, the timer uses the value the variable had when the singleShot was called (started), as
=
copies the data.int localVar = 42; QTimer::singleShot(3000, [=](){ qDebug() << localVar; }); // value copied here localVar = 1337; // does not affect the timer (data already copied)
This prints 42 and not 1337, even though
localVar
changed and when out of scope when the 3s delay was over.@Crag_Hack said in Lambda Evaluation in Signal/Slot Connection or QTimer:
My question is are signal/slot connections and QTimer:singleShots smart enough to tell when you want one behavior or the other?
How should the lambda know what your intentions are?!
C++ does not prevent you from doing non-sense ;-)Capturing by reference - using either
[&]
or[&var]
- in the first example results in undefined behavior and is non-sense, though.Btw:
When using the a lambda affecting a
QObject
class as receiver or inside the lambda, it's recommended to set a context. Otherwise it might result in undefined behavior, as the lambda is not disconnected if the receiver or some object you manipulate inside the lambda is destroyed. -
For the first one I'm not 100% sure, but the second one uses the value
jobDone
has, when the signal is emitted.
As long asjobDone
is a member, it's ok to use... using a local variable there will even throw a warning
like
captured local variable by reference might go out of scope before lambda is called [clazy-lambda-in-connect]
Edit: If I'm not mistaken, the timer uses the value the variable had when the singleShot was called (started), as
=
copies the data.int localVar = 42; QTimer::singleShot(3000, [=](){ qDebug() << localVar; }); // value copied here localVar = 1337; // does not affect the timer (data already copied)
This prints 42 and not 1337, even though
localVar
changed and when out of scope when the 3s delay was over.@Crag_Hack said in Lambda Evaluation in Signal/Slot Connection or QTimer:
My question is are signal/slot connections and QTimer:singleShots smart enough to tell when you want one behavior or the other?
How should the lambda know what your intentions are?!
C++ does not prevent you from doing non-sense ;-)Capturing by reference - using either
[&]
or[&var]
- in the first example results in undefined behavior and is non-sense, though.Btw:
When using the a lambda affecting a
QObject
class as receiver or inside the lambda, it's recommended to set a context. Otherwise it might result in undefined behavior, as the lambda is not disconnected if the receiver or some object you manipulate inside the lambda is destroyed.@Pl45m4 said in Lambda Evaluation in Signal/Slot Connection or QTimer:
Capturing by reference - using either [&] or [&var] - in the first example is totally fine (non-sense, though), but will result in 0/no data, every time.
Um, no, it will/should result in undefined behaviour if you are "lucky" or a crash if you are "unlucky" (or perhaps the luck is the other way round, depends on your POV!).
QString inputString
is a local variable on the stack, and it goes out of scope and is destroyed as soon asfunction()
exits after setting up the timer. Capturing it by reference via&
to the lambda will mean when the slot is executed it tries to access some stack address which is no longer valid. Actually I see you quoted a possible compiler error message for this, that would/should happen for the first example if OP put&
instead of=
there.@Crag_Hack
Not sure what/why you are asking here. You seem to understand=
versus&
(and @Pl45m4's clarifications are correct), you seem to know when to use which, so that's it. It's not Qt/signal/slot which knows anything "smart", it's you as programmer who have to pass the right one! -
@Pl45m4 said in Lambda Evaluation in Signal/Slot Connection or QTimer:
Capturing by reference - using either [&] or [&var] - in the first example is totally fine (non-sense, though), but will result in 0/no data, every time.
Um, no, it will/should result in undefined behaviour if you are "lucky" or a crash if you are "unlucky" (or perhaps the luck is the other way round, depends on your POV!).
QString inputString
is a local variable on the stack, and it goes out of scope and is destroyed as soon asfunction()
exits after setting up the timer. Capturing it by reference via&
to the lambda will mean when the slot is executed it tries to access some stack address which is no longer valid. Actually I see you quoted a possible compiler error message for this, that would/should happen for the first example if OP put&
instead of=
there.@Crag_Hack
Not sure what/why you are asking here. You seem to understand=
versus&
(and @Pl45m4's clarifications are correct), you seem to know when to use which, so that's it. It's not Qt/signal/slot which knows anything "smart", it's you as programmer who have to pass the right one!@JonB said in Lambda Evaluation in Signal/Slot Connection or QTimer:
Um, no, it will/should result in undefined behaviour if you are "lucky" or a crash if you are "unlucky" (or perhaps the luck is the other way round, depends on your POV!). QString inputString is a local variable on the stack, and it goes out of scope and is destroyed as soon as function() exits after setting up the timer
Yes I know but I even tried it, and got 0 for ints and "" for captured strings consequently.
Maybe I was just lucky :D
The behavior is still undefined and not the right way, as I've said before :) -
@JonB said in Lambda Evaluation in Signal/Slot Connection or QTimer:
Um, no, it will/should result in undefined behaviour if you are "lucky" or a crash if you are "unlucky" (or perhaps the luck is the other way round, depends on your POV!). QString inputString is a local variable on the stack, and it goes out of scope and is destroyed as soon as function() exits after setting up the timer
Yes I know but I even tried it, and got 0 for ints and "" for captured strings consequently.
Maybe I was just lucky :D
The behavior is still undefined and not the right way, as I've said before :)@Pl45m4
I don't mean to drag this on, but you wroteCapturing by reference - using either [&] or [&var] - in the first example is totally fine (non-sense, though), but will result in 0/no data, every time.
But it isn't "totally fine" for anyone reading, it's undefined and bad. If you got 0 or empty string that is just whatever happened to be on the stack at the original address in your case in your app on your machine. I might not, you might not tomorrow. Either it's "fine" or it's "undefined and bad".
-
@Pl45m4
I don't mean to drag this on, but you wroteCapturing by reference - using either [&] or [&var] - in the first example is totally fine (non-sense, though), but will result in 0/no data, every time.
But it isn't "totally fine" for anyone reading, it's undefined and bad. If you got 0 or empty string that is just whatever happened to be on the stack at the original address in your case in your app on your machine. I might not, you might not tomorrow. Either it's "fine" or it's "undefined and bad".
-
-
Thanks guys appreciate the help. To clarify I was actually wondering am I correct about this: The lambdas capture the variables immediately upon creation and then are executed using those captures at the timeout of the timer or execution of the slot.
@Crag_Hack
Yes, as I wrote earlier ISTM you do understand lambdas and captures! :) Lambdas do their capture work where they are in the code, e.g. as aconnect()
statement is met to set up a signal/slot. The lambda-slot then uses those variables in its code when the slot is executed at a future time. With an=
the variable's value is copied atconnect()
-time, so the slot will always its value as it was at that instant, while with an&
only a reference to the variable, not its current value, is copied, so when accessed in the slot it will have its value as it is at that time. An&
-captured variable must therefore still be in scope/alive when the lambda body executes, which is not required/not relevant for an=
-captured one. -