Passing a mutable lambda with unique_ptr into a const& std::function - c++

I have got a dispatch function which executes a given lambda in a main thread. For the sake of this question, suppose it looks like the following:
void dispatch(const std::function<void()>& fn) {
fn();
}
I need to load a new object in a new thread without interrupting the main thread. So I do the following: 1) start a new thread and create a new unique pointer inside the thread, 2) call dispatch and propagate the new unique pointer where it belongs.
std::unique_ptr<std::string> foo; // nullptr
// do the loading in a new thread:
std::thread t([&](){
// in the new thread, load new value "Blah" and store it temporarily
auto bar = std::make_unique<std::string>("Blah");
dispatch([bar2 = std::move(bar), &foo]() mutable {
foo = std::move(bar2); // propagate the loaded value to foo
});
});
t.join(); // for the sake of this example
std::cout << "foo = " << *foo << std::endl; // this should say: foo = Blah
Run example online: http://cpp.sh/5zjvm
This code does not compile because the inner lambda in dispatch is mutable and so does not fit into dispatch(const std::function<void()>& fn) which requires a const&.
The lambda, however, needs to be mutable because it needs to call std::move on the unique pointers.
This code could be fixed for example by changing dispatch to:
template <typename Fn>
void dispatch(Fn fn) {
fn();
}
Unfortunately, the dispatch function is an API of a library and I cannot change it.
Is there a way out of this problem without getting rid of unique pointers?

No, that isn't your problem.
Your problem is that your lambda cannot be copied, as it has a unique ptr captured by value in it.
std::function<Sig> type erases down to
Invoke with Sig
Destroy
Copy (and sometimes move)
Cast-back-to-original-type
Your lambda cannot be copied, so cannot be stored in a std::function.
The lazy-coder's solution is:
dispatch([bar2 = std::make_shared<decltype(bar)>(std::move(bar)), &foo]() mutable {
foo = std::move(*bar2);
});
where we shove the non-copyable state into a shared_ptr.

Related

Bind a valid class object pointer to boost::function<>fn. What may happen if invoking the `fn` after the object has been destoryed?

Bind a class member function and a valid class object pointer to boost::function<>fn. What may happen if invoking the fn after the object which the pointer pointed to has been destroyed?
Are there some potential problems that I should be aware of?
Domo code snappet:
class CTest
{
public:
int demo(void){}
};
int main()
{
boost::function<int(void)> fn;
{
CTest ins;
fn = boost::bind(&CTest::demo, &ins);
}
fn();
}
Edited(https://godbolt.org/z/r8EK1G)
Quoted from the comment of j6t
One way to do that is to pass the object by value, not a pointer to the
object. Then a copy of the object would be used during the invocation
fn().
I think there is still a problem that the object 'tes' is out of scope.So passing value is not a good method.
:
#include<functional>
#include<iostream>
class CTest
{
public:
int demo(void){std::cout << "do better" << std::endl;return 0;}
};
template <class T=CTest>
std::function<int(void)> bindWarp(T obj, int (T::*mem_func)(void))
{
return std::bind(mem_func, obj);
}
int main()
{
std::function<int(void)> fn;
{
CTest tes;
fn = bindWarp(tes, &CTest::demo);
}
fn(); //I think there is still a problem that the object 'tes' is out of scope.So passing value is not a good method.
}
You need to ensure that the target object's lifetime exceeds the function object's lifetime. This is easier to express in an obvious way with a lambda function instead of bind. The lambda can explicitely capture the object 'by-value':
std::function<int(void)> fn;
{
CTest tes;
fn = [tes] { tes.demo(); };
}
fn();
With std::bind you can also explicitely express this, and avoid a copy, by writing:
fn = std::bind(&CTest::demo, std::move(tes));
You could also pass to bind by-value instead of by-reference but I would prefer the two constructs above because they make the intention very clear (copying the object or transfering ownership).
If you want to avoid a copy or transfer of ownership, you may use a shared pointer with the lambda instead. You could even go as far as using a weak_ptr within the lambda, so the object is not kept alive for it.
See also this very helpful discussion about the details of binds & lambdas in modern C++: https://stackoverflow.com/a/17545183/21974

C++: How do I "un-capture" non-copyables (eg. unique_ptr) from a lambda?

Consider the following:
unique_ptr<int> foo = make_unique<int>(42);
auto lambda = [bar = move(foo)]()
{
/* Do something with bar */
};
lambda(); // No issues invoking this
cout << "*foo = " << *foo; //Attempts to dereference foo will segfault
Capturing things like a unique_ptr requires use of std::move, so as to maintain the uniqueness of unique_ptr. But what to do when I want to use that same smart pointer after the lambda is destructed? Using foo will give a segfault, and bar is out of scope by that point.
Perhaps unorthodox use of lambda aside, how do I get my unique_ptr back? Is it trapped in the lambda forever?
This can be solved by capturing by reference.
auto lambda = [&]()
{
/* Do something with foo */
};
// or
auto lambda = [&foo]()
{
/* Do something with foo */
};
Allows you to use foo, without actually moving it.
The only caveat with this is it is up to you to ensure the lifetime of the lambda does not exceed that of the pointer. If it can/does, then you should consider a shared ownership method like using a std::shared_ptr instead.
But what to do when I want to use that same smart pointer after the lambda is destructed?
You use std::shared_ptr and don't move something you want to reuse.
auto foo = std::make_shared(42);
auto lambda = [bar=foo]()
{
/* Do something with bar */
};
lambda(); // No issues invoking this
cout << "*foo = " << *foo; // also fine

What is the lifespan of the captured stack allocated variables in lambda functions used as slots?

I need help understanding the way lambda functions work in order to prevent memory leaks when using them. More specifically, I would like to know when foo will be destroyed in the following case:
void MainWindow::onButtonClicked()
{
QTimer *t(new QTimer(this));
bool foo = false;
t->setSingleShot(true);
t->setInterval(1000);
t->start();
connect(t, &QTimer::timeout, [=](){
delete t;
qDebug() << foo;
});
}
What about the case when [&] is used?
An evaluated lambda expression is a functor instance. A functor is an object with operator(). The captured variables are members of that functor object. Their lifetime doesn't change based on their type. Thus, whether you capture references or values, their lifetime is the same. It's your job to ensure that the references are valid - i.e. that the objects they reference haven't been destroyed.
The functor's lifetime is the same as the connection's lifetime. The connection, and thus the functor, will last until either:
QObject::disconnect() is invoked on the return value of QObject::connect(), or
The QObject's life ends and its destructor is invoked.
Taking the above into account, the only valid use of local variable capture-by-reference is when the local variables outlive the connection. Some valid examples would be:
void test1() {
int a = 5;
QObject b;
QObject:connect(&b, &QObject::destroyed, [&a]{ qDebug() << a; });
// a outlives the connection - per C++ semantics `b` is destroyed before `a`
}
Or:
int main(int argc, char **argv) {
QObject * focusObject = {};
QApplication app(argc, argv);
QObject * connect(&app, &QGuiApplication::focusObjectChanged,
[&](QObject * obj){ focusObject = obj; });
//...
return app.exec(); // focusObject outlives the connection too
}
The code in your question is needlessly complex. There's no need to manage such timers manually:
void MainWindow::onButtonClicked() {
bool foo = {};
QTimer::singleShot(1000, this, [this, foo]{ qDebug() << foo; });
}
The important part here is to provide the object context (this) as the 2nd argument of singleShot. This ensures that this must outlive the functor. Conversely, the functor will be destroyed before this is destroyed.
Assuming that you really wanted to instantiate a new transient timer, it is undefined behavior to delete the signal's source object in a slot connected to such a signal. You must defer the deletion to the event loop instead:
void MainWindow::onButtonClicked()
{
auto t = new QTimer(this);
bool foo = {};
t->setSingleShot(true);
t->setInterval(1000);
t->start();
connect(t, &QTimer::timeout, [=](){
qDebug() << foo;
t->deleteLater();
});
}
Both t and foo are copied into the functor object. The lambda expression is a notational shorthand - you could write it explicitly yourself:
class $OpaqueType {
QTimer * const t;
bool const foo;
public:
$OpaqueType(QTimer * t, bool foo) :
t(t), foo(foo) {}
void operator()() {
qDebug() << foo;
t->deleteLater();
}
};
void MainWindow::onButtonClicked() {
//...
connect(t, &QTimer::timeout, $OpaqueType(t, foo));
}
Since a lambda instance is just an object, you can certainly assign it to a variable and get rid of code duplication should more than one signal need to connect to same lambda:
auto f = [&]{ /* code */ };
connect(o, &Class::signal1, this, f);
connect(p, &Class::signal2, this, f);
The type of the lambda is a unique, unutterable, also called opaque, type. You cannot mention it literally - there's no mechanism in the language to do so. You can refer to it via decltype only. Here decltype is the he in he who shall not be named. C++ people just worked in a Harry Potter joke, whether they meant to or not. I won't be convinced otherwise.
A lambda that captures variables is essentially an unnamed data structure, and the captured variables become members of that structure. That data structure is considered callable because it provides an operator() that accepts specified arguments.
If variables are captured by value, members of the lambda hold those values. Those values exist as long as the lambda does, as in your case, as copies of the captured variables - regardless of whether the originally captured variables continue to exist.
It's a bit different for variables captured by reference. In that case, the lambda contains a reference to a variable (e.g. a stack variable), and that becomes a dangling reference if the referred variable ceases to exist. Similarly, if the captured value is a pointer, and what it points at ceases to exist. In these cases, usage of the captured reference or dereferencing the pointer give undefined behaviour.

extend scope of pass by ref object for std::thread

To demonstrate the problem, let me present a short code -
void someMethod() {
// CustomType obj;
const auto obj = getCustomTypeObj();
std::thread([](customType &obj) {
// some delay
obj.doSomething();
obj.close();
// can now be destructed
}).detach();
// similarly for std::async
std::async(std::launch::async, [](customType &obj){
obj.doSomething();
obj.close();
}
// there might not be any use of obj here
// should not be destructed here because std::thread might not get it.
}
In above code, an CustomType type object is constructed for which copy constructor is deleted. So I must pass it by reference everywhere, or create it from scratch in relevant scope. However for 1 scenario I'm currently dealing with, it is not quite possible to create it in relevant scope which is inside std::thread's execution method.
What I'm afraid of is obj might be destructed before std::thread even completes its job and then I've no idea what's going to happen. So how should I solve this problem of extending it's scope to std::thread's lambda.
Btw your code is incorrect, you do not pass your object, so your code should be instead:
auto obj = getCustomTypeObj();
std::thread([](customType &obj) {
// some delay
obj.doSomething();
obj.close();
// can now be destructed
}, std::ref( obj ) ).detach();
To avoid issue with object lifetime pass your object to the lambda or function by value and move your object there:
auto obj = getCustomTypeObj();
std::thread([](customType arg) { // note by value, not reference
// some delay
arg.doSomething();
arg.close();
// arg will be destroyed here
}, std::move( obj ) ).detach(); // object moved
now lambda or function owns that object and it will be destroyed at the end of the function. Here is the live example, I just used std::unique_ptr there instead of customType as type that has copying disabled to validate that moving works.

How to capture a unique_ptr into a lambda expression?

I have tried the following:
std::function<void ()> getAction(std::unique_ptr<MyClass> &&psomething){
//The caller given ownership of psomething
return [psomething](){
psomething->do_some_thing();
//psomething is expected to be released after this point
};
}
But it does not compile. Any ideas?
UPDATE:
AS suggested, some new syntax is required to explicitly specify we need to transfer the ownership to the lambda, I am now thinking about the following syntax:
std::function<void ()> getAction(std::unique_ptr<MyClass> psomething){
//The caller given ownership of psomething
return [auto psomething=move(psomething)](){
psomething->do_some_thing();
//psomething is expected to be released after this point
};
}
Would it be a good candidate?
UPDATE 1:
I will show my implementation of move and copy as following:
template<typename T>
T copy(const T &t) {
return t;
}
//process lvalue references
template<typename T>
T move(T &t) {
return std::move(t);
}
class A{/*...*/};
void test(A &&a);
int main(int, char **){
A a;
test(copy(a)); //OK, copied
test(move(a)); //OK, moved
test(A()); //OK, temporary object
test(copy(A())); //OK, copying temporary object
//You can disable this behavior by letting copy accepts T &
//test(move(A())); You should never move a temporary object
//It is not good to have a rvalue version of move.
//test(a); forbidden, you have to say weather you want to copy or move
//from a lvalue reference.
}
This issue is addressed by lambda generalized capture in C++14:
// a unique_ptr is move-only
auto u = make_unique<some_type>(some, parameters);
// move the unique_ptr into the lambda
go.run([u = move(u)]{do_something_with(u);});
You cannot permanently capture a unique_ptr in a lambda. Indeed, if you want to permanently capture anything in a lambda, it must be copyable; merely movable is insufficient.
This could be considered a defect in C++11, but you would need some syntax to explicitly say that you wanted to move the unique_ptr value into the lambda. The C++11 specification is very carefully worded to prevent implicit moves on named variables; that's why std::move exists, and this is a good thing.
To do what you want will require either using std::bind (which would be semi-convoluted, requiring a short sequence of binds) or just returning a regular old object.
Also, never take unique_ptr by &&, unless you are actually writing its move constructor. Just take it by value; the only way a user can provide it by value is with a std::move. Indeed, it's generally a good idea to never take anything by &&, unless you're writing the move constructor/assignment operator (or implementing a forwarding function).
The "semi-convoluted" solution using std::bind as mentioned by Nicol Bolas is not so bad after all:
std::function<void ()> getAction(std::unique_ptr<MyClass>&& psomething)
{
return std::bind([] (std::unique_ptr<MyClass>& p) { p->do_some_thing(); },
std::move(psomething));
}
A sub-optimal solution that worked for me was to convert the unique_ptr to a shared_ptr and then capture the shared_ptr in the lambda.
std::function<void()> getAction(std::unique_ptr<MyClass> psomething)
{
//The caller given ownership of psomething
std::shared_ptr<MyClass> psomethingShared = std::shared_ptr<MyClass>(std::move(psomething));
return [psomethingShared]()
{
psomethingShared->do_some_thing();
};
}
I used this really dodgy workaround, which involves sticking the unique_ptr inside a shared_ptr. This is because my code required a unique_ptr (due to an API restriction) so I couldn't actually convert it to a shared_ptr (otherwise I'd never be able to get my unique_ptr back).
My justification for using this abomination is that it was for my test code, and I had to std::bind a unique_ptr into the test function call.
// Put unique_ptr inside a shared_ptr
auto sh = std::make_shared<std::unique_ptr<Type>>(std::move(unique));
std::function<void()> fnTest = std::bind([this, sh, input, output]() {
// Move unique_ptr back out of shared_ptr
auto unique = std::move(*sh.get());
// Make sure unique_ptr is still valid
assert(unique);
// Move unique_ptr over to final function while calling it
this->run_test(std::move(unique), input, output);
});
Now calling fnTest() will call run_test() while passing the unique_ptr to it. Calling fnTest() a second time will result in an assertion failure, because the unique_ptr has already been moved/lost during the first call.
One also need to know, that lambdas capturing unique_ptr cannot be converted into std::function because std::function requires that the callable object is copyable.
auto lambdaWithoutCapture = [](){return 1;}; //Can be std::function
auto lambdaWithCapture = [=](){return 1;}; //Can be std::function
auto lambdaWithCapture2 = [&](){return 1;}; //Can be std::function
auto lambdaWithCapture3 = [uptrProblematic = std::move(uptrProblematic)]() mutable {return 1;}; //Can't be std::function
Therefore, if you don't have to specify return type of the function, you can use such approach which does not use std::function. But you need to know, that this will only work in local scope. You can't declare auto workerFactory(); in header file, as this will raise compilation error.
auto workerFactory()
{
std::unique_ptr uptrProblematic = std::make_unique<int>(9);
int importantData = 71;
return [=, uptrProblematic = std::move(uptrProblematic)](std::string input) mutable -> int {
std::cout << "Problematic variable is equal to: " << *uptrProblematic << "\n";
std::cout << "Important data is equal to: " << importantData << "\n";
std::cout << "Input equal to: " << input << "\n";
return 9;
};
}
int main()
{
auto worker = workerFactory();
worker("Test");
}