int i = 9;
struct_variable.f = [i](T struct_variable&) {
do_something_with_capture_variable(i);
...
struct_variable.f = another_compatible_std_function;
//do something else, but never use captured variable after here
...
};
struct_variable.f(struct_variable);
The lambda function is saved as member struct_variable.f(which is also typed std::function), and in the callback, struct_variable.f is replaced by another_compatible_std_function after finish using captured variable.
Is this practice guaranteed to be safe?
Code portion of the lambda is compiled into machine code and during assignment only pointer-to-function that point to that code is assigned. So as long as you no longer use captured variables it should be safe to re-assign the variable holding the lambda because no running code will be altered.
Related
I have a
std::string
and pass that by reference to a function.
The function dereferences the parameter (pointer) into a local std::string for ease of working.
I update the local string's value and then update the pointer.
Have I just pointed to something that's gone out of scope? Or have I updated the underlying value of the string pointed to by string abc in the example below?
BTW It works but I'm concerned I'm breaking C++ rules.
int main() {
std::string abc = R"(This is a string "raw" literal)";
updateStringValue(&abc);
log_v(abc.c_str());
}
void updateStringValue(std::string *theData) {
std::string localString = (*theData);
localString.append("some other value");
*theData = localString;
}
I realise that the local string "localString" only survives the function but has the amended value really been retained (or maybe it's still floating around in memory but now out of scope once the function ends)?
I expected this to work (it did) but am having doubts.
Previously I did not dereference the incoming data's pointer into a local string but the resulting syntax was more confusing (to others) so I did it this way to simplify the overall structure.
I've definitely confused myself now.
It works fine. *theData = localString; invokes copy assignment, so the caller's string is changed to be equivalent to (but not the same object as) localData. After that, localData gets cleaned when the function returns, but that's fine, the copy has already occurred.
To be clear, you did not pass by reference (that would be void updateStringValue(std::string& theData), with an &, not a *, and no need for the caller to explicitly take the address of their local variable). Passing-by-reference would save you the need to manually dereference the pointer.
Also note, there's no need to make the copy here. You could just call:
theData->append("some other value");
or if you used a true reference:
theData.append("some other value");
and save the local copy and copy-back entirely.
I have always assumed lambda were just function pointers, but I've never thought to use capture statements seriously...
If I create a lambda that captures by copy, and then move that lambda to a completely different thread and make no attempt to save the original objects used in the lambda, will it retain those copies for me?
std::thread createThread() {
std::string str("Success");
auto func = [=](){
printf("%s", str.c_str());
};
str = "Failure";
return std::thread(func);
}
int main() {
std::thread thread = createThread();
thread.join();
// assuming the thread doesn't execute anything until here...
// would it print "Success", "Failure", or deference a dangling pointer?
return 0;
}
It is guaranteed to print Success. Capture-by-copy does exactly what it says. It make a copy of the object right there and stores this copy as part of the closure object. The member of the closure object created from the capture lives as long as the closure object itself.
A lambda is not a function pointer. Lambdas are general function objects that can have internal state, which a function pointer can't have. In fact, only capture-less lambdas can be converted to function pointers and so may behave like one sometimes.
The lambda expression produces a closure type that basically looks something like this:
struct /*unnamed1*/ {
/*unnamed1*/(const /*unnamed1*/&) = default;
/*unnamed1*/(/*unnamed1*/&&) = default;
/*unnamed1*/& operator=(const /*unnamed1*/&) = delete;
void operator()() const {
printf("%s", /*unnamed2*/.c_str());
};
std::string /*unnamed2*/;
};
and the lambda expression produces an object of this type, with /*unnamed2*/ direct-initialized to the current value of str. (Direct-initialized meaning as if by std::string /*unnamed2*/(str);)
You have 3 situations
You can be design guarantee that variables live longer then the thread, because you synchronize with the end of the thread before variables go out of scope.
You know your thread may outlive the scope/life cycle of your thread but you don't need access to the variables anymore from any other thread.
You can't say which thread lives longest, you have multiple thread accessing your data and you want to extend the live time of your variables
In case 1. Capture by reference
In case 2. Capture by value (or you even use move) variables
In case 3. Make data shared, std::shared_ptr and capture that by value
Case 3 will extend the lifetime of the data to the lifetime of the longest living thread.
Note I prefer using std::async over std::thread, since that returns a RAII object (a future). The destructor of that will synchronize with the thread. So you can use that as members in objects with a thread and make sure the object destruction waits for the thread to finish.
While using some local lambda objects in a C++11 function I was tempted to declare them as const static auto lambda = ... just to let the compiler know that there is just one std::function object needed (and possibly optimize the call and/or inline it) but I realized that capturing local values by reference in this circumstance leads to weird behavior.
Consider the following code:
void process(const Data& data, const std::function<void(DataElement&>& lambda) {
...
}
void SomeClass::doSomething()
{
int foo = 0;
const static auto lambda = [&foo] () { .... ++foo; .... }
process(data, lambda);
}
This doesn't work with multiple invocations of doSomething() but the mechanics is not clear.
Is foo bound at the first invocation and then kept bound to a stack address which becomes invalid on successive invocations?
Am I forced so drop static in this circumstance?
Where is this behavior specified in the standard? Considering it's a static variable where is it constructed? Lazily on first invocation of doSomething() (so that the first invocation works) or at program start?
A static function-scope variable is initialised "lazily," when control flow first reaches its declaration. This means that the capture by reference does indeed bind to the foo currently on stack, and when the call terminates, that binding becomes dangling.
Don't try to help the compiler too much; making lambda static looks like a micro-optimisation, with very bad side effects. There's next to no overhead involved in actually creating a closure object, and the compiler can easily inline it regardless of whether it's static or not.
Not to mention the fact that you're not saving on creating the std::function object even with your approach. The type of a lambda expression is an unnamed closure object, not std::function. So even if lambda is static, the std::function object is created in each call anyway (unless the whole thing is inlined).
This doesn't work with multiple invocations of doSomething() but the mechanics is not clear.
This is because foo is allocated on the stack. The exact address of foo depends on the call stack that led to the invocation of doSomething. In other words, the address of foo is likely to be different between function invocations, unless the call stack is exactly the same.
I capture local bool value by reference to lambda and the first time it gets captured the value is unassigned (some random value). Why?
bool singleConfirmed=false;
button->addTouchEventListener([text, &singleConfirmed](Ref*, Widget::TouchEventType type)
{
if (type != Widget::TouchEventType::ENDED) return;
if (!singleConfirmed)
{
cocostudio::ActionManagerEx::getInstance()->playActionByName(R_tutorialDialog.c_str(), "MoveToTop");
text->setString(G_str("Tutorial_Single/Multiplayer"));
singleConfirmed=true;
return;
}
else
{
cocostudio::ActionManagerEx::getInstance()->playActionByName(R_tutorialDialog.c_str(), "SwipeToLeft");
text->setString(G_str("Tutorial_Single/Multiplayer"));
return;
}
});
There's not quite enough context in the provided code to be certain, but as sharth hinted in the comments, the problem is almost certainly that singleConfirmed is an automatic, local variable that has gone out of scope (been destroyed) by the time the lambda is invoked, which means the lambda will be working with a wild reference. To solve this, you need to use something that won't be destroyed when the scope exits. That means dynamic allocation. Dynamic allocation means you'll need to deallocate when the lambda is destroyed. The simplest way to ensure that is to use a smart pointer. Putting that all together, my suggestion is to store the bool in a shared_ptr and capture it by value:
auto singleConfirmed = std::make_shared<bool>(false);
button->addTouchEventListener([text, singleConfirmed](Ref*, Widget::TouchEventType type)
{
// ...
}
(text appears to be some sort of pointer that you're capturing by value, so that should be ok as long as it doesn't get deleted before the lambda goes away)
As swarth's and dlf's comment suggest, the lambda is almost certainly being run outside the scope of the local variable singleConfirmed. The name of addTouchEventListener strongly suggests that the function object will be stored and executed in response to some user event later, and not executed now synchronously.
When a lambda is going to be executed out of the scope in which it was created, it doesn't make sense to capture variables by reference, because those variables are going to be out of scope by the time it's executed, and therefore, using the reference is invalid.
Instead, you should capture by value. However, by default, value-captured variables are const, so you cannot assign to it inside the lambda, as it seems you want to do here. You need to declare the lambda mutable to make the value-captured variables non-const:
bool singleConfirmed = false;
button->addTouchEventListener(
[text, singleConfirmed](Ref*, Widget::TouchEventType type) mutable {
// everything in here stays the same
})
I have a future to which I want to pass a lambda to run when it is complete, but the scope will have changed by the time the lambda executes; what happens to the captured value? For example
bool* MakeThen(Concurrency::completion_future& future)
{
bool * isFinished = new bool(false);
future.then([=](){ *isFinished = true; });
return isFinished;
}
By the time that lambda actually executes, the function might have finished. So what will happen? Is capturing by value just like binding a bunch of variables?
Your lambda captures the isFinished pointer by value, and the object to which it points is on the free store. So it's fine. There is no local object being referred to in the lambda.
As long as you haven't done delete isFinished;, it's safe.