pass a lambda that captures a unique_pointer - c++

I'm using lambda generalized capture to move a unique_ptr into a lambda (https://stackoverflow.com/a/16968463/118958). I want to pass this lambda elsewhere to set a callback function, but I'm not sure how.
Here's a small example with what I'm trying to do that fails to compile:
void set_callback(std::function<void(void)> cb);
void move_callback(std::function<void(void)> &&cb);
void test() {
auto a_ptr = std::make_unique<int>(10);
auto lambda = [a_ptr = std::move(a_ptr)] () {};
// set_callback(lambda); // I understand why this wouldn't work
move_callback(std::move(lambda)); // but I would expect this to be OK
}
Any idea on how to do something like the above?

std::function must be both CopyConstructible and CopyAssignable, which means that it must be able to copy its target. Unfortunately, since your lambda is not copyable, you cannot store it in a std::function.
You will have to resort to another implementation of callable type-erasure that works with movable-only objects.

Related

When using std::move with a lambda when does the move happen

If I create a lambda in a function and capture a variable to the lambda using std::move, when does the move happen? Is it when the lambda is created or when the lambda is executed?
Take the following code for example ... when do the various moves happen? Is it thread safe if myFunction is called on one thread and testLambda is executed on another thread?
class MyClass {
private:
// Only accessed on thread B
std::vector<int> myStuff;
// Called from thread A with new data
void myFunction(const std::vector<int>&& theirStuff) {
// Stored to be called on thread B
auto testLambda = [this, _theirStuff{ std::move(theirStuff) }]() {
myStuff = std::move(_theirStuff);
};
// ... store lambda
}
// Elsewhere on thread A
void someOtherFunction() {
std::vector<int> newStuff = { 1, 2, .... n };
gGlobalMyClass->myFunction(std::move(newStuff));
}
If I create a lambda in a function and capture a variable to the lambda using std::move, when does the move happen? Is it when the lambda is created or when the lambda is executed?
If you had written what I believe you intended to write, then the answer would be: both. Currently, the answer is: neither. You have a lambda capture _theirStuff { std::move(theirStuff) }. This basically declares a member of the closure type, which will be initialized when the closure object is created as if it were
auto _theirStuff { std::move(theirStuff) };
You also have
myStuff = std::move(_theirStuff);
in the lambda body.
However, your parameter theirStuff is actually an rvalue reference to a const std::vector<int>. Thus, _theirStuff { std::move(theirStuff) } is not actually going to perform a move, because a const std::vector cannot be moved from. Most likely, you wanted to write std::vector<int>&& theirStuff instead. Furthermore, as pointed out by #JVApen in the comments below, your lambda is not mutable. Therefore, _theirStuff will actually be const as well, and, thus, also cannot be moved from. Consequently, your code above, despite all the std::move, will actually make a copy of the vector every time. If you had written
void myFunction(std::vector<int>&& theirStuff)
{
auto testLambda = [this, _theirStuff { std::move(theirStuff) }]() {
myStuff = std::move(_theirStuff);
};
}
You would be moving theirStuff into _theirStuff when the closure object is created. And you would be copying _theirStuff into myStuff when the lambda is called. If you had written
void myFunction(std::vector<int>&& theirStuff)
{
auto testLambda = [this, _theirStuff { std::move(theirStuff) }]() mutable {
myStuff = std::move(_theirStuff);
};
}
Then you would be moving theirStuff into _theirStuff when the closure object is created. And you would be moving _theirStuff into myStuff when the lambda is called. Note that, as a consequence, your lambda then cannot really be called twice. I mean, it can, but it will only really work once since _theirStuff will be empty after the first time the lambda is called…
Also, note that above description is only valid for the particular combination of types in your example. There is no general definition of what it actually means to move an object. What it means to move an object is entirely up to the particular type of the object. It may not even mean anything. std::move itself does not really do anything. All it does is cast the given expression to an rvalue reference. If you then initialize another object from the result of std::move, or assign the result to an object, overload resolution will pick a move constructor or move assignment operator—if one exists—instead of the normal copy constructor or copy assignment operator. It is then up to the implementation of the move constructor/move assignment operator of the respective type to actually perform a move, i.e., do whatever it is that's supposed to be done for the particular type in case of initialization or assignment from an rvalue. So, in a way, what you do when you apply std::move is that you advertise the respective object as "this may be moved from". Whether or not it actually will be moved from (and, if so, what that actually means) is up to the implementation. In the particular case of std::vector, the move constructor/move assignment operator, by definition, guarantee that not only the contents of the original vector will be taken over from the original object, but also that the original object will be empty afterwards. In many other cases, it may be undefined behavior to do anything with an object that was moved from (except, maybe, destroy it; that one can be pretty much taken for granted as a type that doesn't at least allow that would be pretty much useless; typically, you will at least be able to assign a new value to an object that was moved from, but even that is not guaranteed in general). You always have to check for the particular type at hand what condition an object is guaranteed to be in after having been moved from…

Why can't I move an element into vector of non-copyables?

The compiler tells me I'm trying to access a deleted function (i.e. the copy constructor of a lambda expression). But I don't see where.
std::vector<std::function<void()>> tasks;
std::packaged_task<int()> task{ [] { return 1; } };
tasks.emplace_back(
[ t = std::move(task) ] () mutable { t(); });
(code is also here)
(I'm trying to find out why they use shared_ptr<task> in https://www.slideshare.net/GlobalLogicUkraine/c11-multithreading-futures).
On Gcc and MSVC I get the same error - I fear I'm doing something wrong...
error: use of deleted function
'main()::<lambda()>::<lambda>(const main()::<lambda()>&)'
Why can't I emplace this std::function onto the vector?
From cppreference:
F must meet the requirements of Callable and CopyConstructible
Where F is the function type used to construct the std::function. However, std::packaged_task is not copy constructible. Thus in the capture list, t is not copy constructible, and is a non-static member of the lambda, making the implicit copy constructor for the lambda deleted.
Short answer: Lambdas and std::packaged_task are not std::functions.
Long answer, you cannot move a std::packaged_task into a std::function
Here's what I'm offering as a solution:
std::vector<std::packaged_task<int()>> tasks;
std::packaged_task<int()> task{ [] () mutable { return 1; } };
tasks.emplace_back( std::move(task) );
If you actually need a std::function, and not just any callable, you'll have to bind a lambda into a std::function
The constructor of std::function requires the passed function object to be CopyConstructible, but std::packaged_task<F> is not (for any F). std::function performs type erasure where the dynamic type is not visible in the static type. Consider e.g.:
int invoke(std::function<int()> f) { return f(); }
int main()
{
std::packaged_task<int()> p{/*etc*/};
auto l = [] { return 5; };
std::function<int()> f( /* either p or l */ );
std::cout << invoke(f) << '\n';
}
The call to invoke requires copying f (pass by value). However, f is copyable if it has been made from l, but not copyable if it has been made from p, and this has nothing to do with the static type of f. There are basically three approaches to this problem:
Forbid copying of std::function at compile time.
Allow copying std::function at compile time, but throw a run time error if the contained type is not copyable.
Allow copying std::function at compile time, and require any function object that you put into it to be copyable.
Approach #1 is very restrictive on how functions may be stored, passed and shared, and basically forbids common use cases in favor of the uncommon case of using a non-copyable function object.
Approach #2 is problematic because users would need to be educated that copying a std::function may fail in some cases and use great diligence when writing their code. Also, if the design requires sharing functions they may need to be wrapped in a std::shared_ptr. And if they need to be copied and may be stateful, it becomes even worse.
No matter how you view approach #3, it is the one that was standardized. But in light of the above-mentioned problems, it is also easily to defend.
As a matter of fact, I have written a unique_function class template that uses approach #1 for my current project because for us the use case of storing non-copyable asynchronous task objects is quite common, and copying or sharing such a task isn't necessary.

Is it possible to pass a lambda with captured non-copyable (moved) value?

I am trying to make a callable object, which essentially has a bound unique_ptr inside. I've read How to capture a unique_ptr into a lambda expression?
But I cannot pass the lambda further. The below code fails to compile indicating that copying unique_ptr is taking place. I don't quite understand why would it try to copy the unique_ptr.
#include <iostream>
#include <memory>
#include <functional>
using namespace std;
void f(std::function<void(int)> unary) {
unary(42);
unary(1337);
}
struct A{
void woof(int i) {cout<< "woof " << i << "\n";}
};
int main(){
auto another_unique = make_unique<A>();
auto bound_p = [ p = move(another_unique) ] (int i) {p->woof(i);};
bound_p(5);
f( bound_p ); // does not compute
}
I've also tried defining lambda inline within the call to f, so that it would be a temporary object, and it could be moved into f. But the error was the same.
Is it possible to bind/wrap such object into callables and pass those around?
My use case hinges on unique_ptr, but I suspect any object with deleted copy c'tor will exhibit the same problem. If I am wrong then please tell me or edit the topic to be less general.
You cannot put any move-only type in std::function. That class requires the type to be copyable.
A better way to handle this would be to make f a template which will call the given parameter, rather than using the type-erased std::function. function should be used for cases where you want to keep a copy of the functor around, not when you just want to call it.

Moving a lambda: once you've move-captured a move-only type, how can the lambda be used? [duplicate]

This question already has answers here:
How to create an std::function from a move-capturing lambda expression?
(3 answers)
Closed 7 years ago.
This answer explains how to move-capture a variable within a lambda in C++14.
But once you've move-captured an un-copyable object (such as a std::unique_ptr) within a lambda, you cannot copy the lambda itself.
This would be fine if you could move the lambda, but I get a compile error when trying to do so:
using namespace std;
class HasCallback
{
public:
void setCallback(std::function<void(void)>&& f)
{
callback = move(f);
}
std::function<void(void)> callback;
};
int main()
{
auto uniq = make_unique<std::string>("Blah blah blah");
HasCallback hc;
hc.setCallback(
[uniq = move(uniq)](void)
{
std::cout << *uniq << std::endl;
});
hc.callback();
}
This produces the following error with g++ (I've attempted to copy only the relevant line):
error: use of deleted function ‘main()::<lambda()>::<lambda>(const main()::<lambda()>&’
...implying, I think, that my attempt to move the lambda has failed.
clang++ gives a similar error.
I tried explicitly moveing the lambda (even though it's a temporary value), but that did not help.
EDIT: The answers below adequately address the compile errors produced by the above code. For an alternate approach, simply release the unique pointer's target value into a std::shared_ptr, which can be copied. (I'm not writing this as an answer, because that would assume that this is an XY problem, but the underlying reason why unique_ptr can't be used in a lambda that gets converted to a std::function is important to understand.)
EDIT 2: Hilariously enough, I just realized auto_ptr would actually do the right thing here (!), as far as I can tell. It acts essentially like unique_ptr, but allows copy-construction in place of move-construction.
You can move the lambda, that's fine. That's not what your problem is though, you're trying to instantiate a std::function with a noncopyable lambda. And the:
template< class F >
function( F f );
constructor of function does:
5) Initializes the target with a copy of f.
This is because std::function:
satisfies the requirements of CopyConstructible and CopyAssignable.
Since function has to be copyable, everything you put into it must also be copyable. And a move-only lambda does not meet that requirement.
std::function is not a lambda! It's a wrapper that can be constructed from any type of callable, including a lambda. std::function requires that the callable be copy-constructible, which is why your example fails.
A move-only lambda can be moved again as shown below.
template<typename F>
void call(F&& f)
{
auto f1 = std::forward<F>(f); // construct a local copy
f1();
}
int main()
{
auto uniq = make_unique<std::string>("Blah blah blah");
auto lambda = [uniq = move(uniq)]() {
std::cout << *uniq << std::endl;
};
// call(lambda); // doesn't compile because the lambda cannot be copied
call(std::move(lambda));
}
Live demo

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");
}