How to Create an Std::Function from a Move-Capturing Lambda Expression

How to create an std::function from a move-capturing lambda expression?

template<class F> function(F f);

template <class F, class A> function(allocator_arg_t, const A& a, F f);

Requires: F shall be CopyConstructible. f shall be Callable for argument types ArgTypes and return type R. The copy constructor and destructor of A shall not throw exceptions.

§20.9.11.2.1 [func.wrap.func.con]

Note that operator = is defined in terms of this constructor and swap, so the same restrictions apply:

template<class F> function& operator=(F&& f);

Effects: function(std::forward<F>(f)).swap(*this);

§20.9.11.2.1 [func.wrap.func.con]

So to answer your question: Yes, it is possible to construct a std::function from a move-capturing lambda (since this only specifies how the lambda captures), but it is not possible to construct a std::function from a move-only type (e.g. a move-capturing lambda which move-captures something that is not copy constructible).

Move capture in lambda

Generalized lambda capture in C++14

In C++14 we will have the so called generalized lambda capture. This enables move capture. The following will be legal code in C++14:

using namespace std;

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

Also note if you need to move object from lambda to some other function you need to make lambda mutable.

go.run( [ u = move(u) ] mutable { do_something_with( std::move(u) ); } );

The generalized lambda capture is much more general in the sense that captured variables can be initialized with anything like so:

auto lambda = [value = 0] mutable { return ++value; };

In C++11 this is not possible yet, but with some tricks that involve helper types. Fortunately, the Clang 3.4 compiler already implements this awesome feature. The compiler will be released December 2013 or January 2014, if the recent release pace will be kept.

UPDATE: The Clang 3.4 compiler was released on 6 Jan 2014 with the said feature.

A workaround for move capture

Here's an implementation of a helper function make_rref which helps with artificial move capture

#include <cassert>
#include <memory>
#include <utility>

template <typename T>
struct rref_impl
{
rref_impl() = delete;
rref_impl( T && x ) : x{std::move(x)} {}
rref_impl( rref_impl & other )
: x{std::move(other.x)}, isCopied{true}
{
assert( other.isCopied == false );
}
rref_impl( rref_impl && other )
: x{std::move(other.x)}, isCopied{std::move(other.isCopied)}
{
}
rref_impl & operator=( rref_impl other ) = delete;
T && move()
{
return std::move(x);
}

private:
T x;
bool isCopied = false;
};

template<typename T> rref_impl<T> make_rref( T && x )
{
return rref_impl<T>{ std::move(x) };
}

And here's a test case for that function that ran successfully on my gcc 4.7.3.

int main()
{
std::unique_ptr<int> p{new int(0)};
auto rref = make_rref( std::move(p) );
auto lambda =
[rref]() mutable -> std::unique_ptr<int> { return rref.move(); };
assert( lambda() );
assert( !lambda() );
}

The drawback here is that lambda is copyable and when copied the assertion in the copy constructor of rref_impl fails leading to a runtime bug. The following might be a better and even more generic solution because the compiler will catch the error.

Emulating generalized lambda capture in C++11

Here's one more idea, on how to implement generalized lambda capture. The use of the function capture() (whose implementation is found further down) is as follows:

#include <cassert>
#include <memory>

int main()
{
std::unique_ptr<int> p{new int(0)};
auto lambda = capture( std::move(p),
[]( std::unique_ptr<int> & p ) { return std::move(p); } );
assert( lambda() );
assert( !lambda() );
}

Here lambda is a functor object (almost a real lambda) which has captured std::move(p) as it is passed to capture(). The second argument of capture is a lambda which takes the captured variable as an argument. When lambda is used as a function object, then all arguments that are passed to it will be forwarded to the internal lambda as arguments after the captured variable. (In our case there are no further arguments to be forwarded). Essentially, the same as in the previous solution happens. Here's how capture is implemented:

#include <utility>

template <typename T, typename F>
class capture_impl
{
T x;
F f;
public:
capture_impl( T && x, F && f )
: x{std::forward<T>(x)}, f{std::forward<F>(f)}
{}

template <typename ...Ts> auto operator()( Ts&&...args )
-> decltype(f( x, std::forward<Ts>(args)... ))
{
return f( x, std::forward<Ts>(args)... );
}

template <typename ...Ts> auto operator()( Ts&&...args ) const
-> decltype(f( x, std::forward<Ts>(args)... ))
{
return f( x, std::forward<Ts>(args)... );
}
};

template <typename T, typename F>
capture_impl<T,F> capture( T && x, F && f )
{
return capture_impl<T,F>(
std::forward<T>(x), std::forward<F>(f) );
}

This second solution is also cleaner, because it disables copying the lambda, if the captured type is not copyable. In the first solution that can only be checked at runtime with an assert().

Passing a lambda with moved capture to function

The error happens because your lambda has non-copyable captures, making the lambda itself not copyable. std::function requires that the wrapped object be copy-constructible.

If you have control over call_func, make it a template:

template<typename T>
void call_func(T&& func)
{
func();
}

int main()
{
std::fstream fs{"test.txt", std::fstream::out};
auto lam = [fs = std::move(fs)] { const_cast<std::fstream&>(fs).close(); };
call_func(lam);
}

Following is my take on your idea in (2). Since std::function requires the wrapped object to be copy-constructible, we can make our own function wrapper that does not have this restriction:

#include <algorithm>
#include <fstream>
#include <iterator>
#include <utility>
#include <memory>
#include <sstream>
#include <vector>

template<typename T>
void call_func(T&& func) {
func();
}

// All functors have a common base, so we will be able to store them in a single container.
struct baseFunctor {
virtual void operator()()=0;
};

// The actual functor is as simple as it gets.
template<typename T>
class functor : public baseFunctor {
T f;
public:
template<typename U>
functor(U&& f)
: f(std::forward<U>(f))
{}
void operator()() override {
f();
}
};

// In C++17 you don't need this: functor's default constructor can already infer T.
template<typename T>
auto makeNewFunctor(T&& v) {
return std::unique_ptr<baseFunctor>(new functor<T>{std::forward<T>(v)});
}

int main() {
// We need to store pointers instead of values, for the virtual function mechanism to behave correctly.
std::vector<std::unique_ptr<baseFunctor>> functors;

// Generate 10 functors writing to 10 different file streams
std::generate_n(std::back_inserter(functors), 10, [](){
static int i=0;
std::ostringstream oss{"test"};
oss << ++i << ".txt";
std::fstream fs{oss.str(), std::fstream::out};
return makeNewFunctor([fs = std::move(fs)] () mutable { fs.close(); });
});

// Execute the functors
for (auto& functor : functors) {
call_func(*functor);
}
}

Note that the overhead from the virtual call is unavoidable: Since you need functors with different behavior stored in the same container, you essentially need polymorphic behavior one way or the other. So you either implement this polymorphism by hand, or use virtual. I prefer the latter.

Capturing lambda in std::function results in extra copies

AFAICT the problem is the const of the foo() argument. When you capture t inside foo(const Test& t), then the type of that capture inside the lambda is also const. Later when you forward the lambda, the lambda's move constructor will have no choice but copy, not move, the capture. You cannot move from const.
After changing foo to foo(Test& t) I get:

main()
Test: Constructor
foo()
Test: Copy Constructor
queue()
Test: Move Constructor
Test: Move Constructor
Test: Destructor
Test: Destructor
dequeue()
_foo()
t={a=20}
Test: Destructor
main() return
Test: Destructor

Alternative solution, mentioned in https://stackoverflow.com/a/31485150/85696, is to use capture in the form [t=t].

With move-capture and two other changes it is possible to eliminate this remaining copy constructor too:

- void foo(const Test& t) {
+ void foo(Test t) {
...
- queue([t](){
+ queue([t = std::move(t)](){
...
- foo(test1);
+ foo(std::move(test1));
main()
Test: Constructor
Test: Move Constructor
foo()
Test: Move Constructor
queue()
Test: Move Constructor
Test: Move Constructor
Test: Destructor
Test: Destructor
Test: Destructor
dequeue()
_foo()
t={a=20}
Test: Destructor
main() return
Test: Destructor

How to create an std::function from a move-capturing lambda expression?

template<class F> function(F f);

template <class F, class A> function(allocator_arg_t, const A& a, F f);

Requires: F shall be CopyConstructible. f shall be Callable for argument types ArgTypes and return type R. The copy constructor and destructor of A shall not throw exceptions.

§20.9.11.2.1 [func.wrap.func.con]

Note that operator = is defined in terms of this constructor and swap, so the same restrictions apply:

template<class F> function& operator=(F&& f);

Effects: function(std::forward<F>(f)).swap(*this);

§20.9.11.2.1 [func.wrap.func.con]

So to answer your question: Yes, it is possible to construct a std::function from a move-capturing lambda (since this only specifies how the lambda captures), but it is not possible to construct a std::function from a move-only type (e.g. a move-capturing lambda which move-captures something that is not copy constructible).

Move capture of parameter in lambda

Move semantics only work on mutable objects. You will need to make your lambda mutable:

auto func_lambda = [b = std::move(b)]() mutable {
f(std::move(b));
};

However you can only call such lambda once. If you want to call it multiple time, you'll have to generate values or use std::exchange(b, B{})

lambda capture of std function

What am I doing wrong?

getAnd returns a function object from a member function, which captures and accesses members.

You call that member function on a local variable, and return the resulting function object to the outside of the scope. The members pointed by the function object no longer exist and calling the function object results in undefined behaviour.

When I replace funcs with funcs_outside, everything works well.

funcs_outside is a global object and you access it within its lifetime, so there is no problem.

how can I fix this?

You could for example capture a copy of the member instead:

return [funcs = this->funcs](const State &s)

lambda move capture for std::thread

template<class Function, class... Args, class Rep, class Period>
void Timer::setInterval(const std::chrono::duration<Rep, Period>& sleep_duration, Function&& f, Args&&... args) {
this->clear = false;
std::thread t([this, f=std::move(f), sleep_duration, args = std::make_tuple(std::forward<Args>(args)...))]() {
while(true) {
if (this->clear) return;
std::this_thread::sleep_for(sleep_duration);
if(this->clear) return;
std::apply(f, args);
}
});
t.detach();
}

That should do correct capturing. Note that as the timer goes off more than once, you don't want to further-forward data into the f.

But that still doesn't work because you didn't capture this.

Really, you should do a proper cv/mutex and not leak threads.

class Timer {
std::condition_variable cv;
mutable std::mutex m;
bool clear = false;
std::thread active_thread;
void dispose_thread() {
if (active_thread.joinable()) {
stop();
active_thread.join();
}
clear = false; // safe, because all threads are dead
}
public:
template<class Function, class... Args, class Rep, class Period>
[[maybe_unused]] void setInterval(const std::chrono::duration<Rep, Period>& sleep_duration, Function&& f, Args&&... args);
auto lock() const {
return std::unique_lock( m );
}
void stop() {
auto l = lock();
clear = true;
cv.notify_all();
}
~Timer() { dispose_thread(); }
};

template<class Function, class... Args, class Rep, class Period>
void Timer::setInterval(const std::chrono::duration<Rep, Period>& sleep_duration, Function&& f, Args&&... args) {
dispose_thread();
// capture everything explicitly; when messing with threads,
// best to understand state
active_thread = std::thread(
[this, f = std::move(f), sleep_duration, args = std::make_tuple(std::forward<Args>(args)...)]()
{
while(true) {
{
auto l = lock();
// return value here isn't useful, but the function is:
(void)cv.wait_for(l, sleep_duration, [&]{ return clear; });
if (clear)
return;
}
std::apply(f, args);
}
}
);
}

and .stop() and .join() in the destructor.

Live example.

This prevents you from leaking threads and outliving your timer object and doesn't make you wait for a timer interval to set a new interval and the like.

You should also set up sleep_until time points, as if you want to do something every second and the f function takes 0.1 seconds, this will actually repeat every 1.1 seconds.

If you want to maintain multiple worker threads in one Timer object, I'd switch to using std::futures in a vector, and maybe sweeping them to clean up ones that are finished at various intervals.

The idea of waiting for existing tasks to cleanup before starting a new tasks has value to me. If you don't like that, you could have the threads report back that they are ready to clean up, and clean them up lazily later. But this also requires multiple clear variables, so the threads that are delayed don't get the wrong value when the next thread is ready to go.

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

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.

Lambda capture-by-value while transfering ownership

A lambda is not a std::function.

You are right that c++14 allows moving something into a lambda, and even move said lambda afterwards.

A std::function on the other hand requires the callable to be Copy Constructable and Copy Assignable.

From cppreference

Class template std::function is a general-purpose polymorphic function wrapper. Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.

The stored callable object is called the target of std::function. If a std::function contains no target, it is called empty. Invoking the target of an empty std::function results in std::bad_function_call exception being thrown.

std::function satisfies the requirements of CopyConstructible and CopyAssignable.

Some ideas for a possible solution could be:

  • Create your own function wrapper (or use a library) that supports move-only callables.

  • Wrap you std::unique_pre in a std::shared_ptr and capture that.

The first option seems much more robust, but just to show the idea for a quick dirty work-around.

#include <memory>
#include <functional>

int main() {
auto moveonly = std::make_unique<int>(5);
auto wrapped = std::make_shared<std::unique_ptr<int>>(std::move(moveonly));

auto lam = [wrapped]() {
return *(*wrapped);
};

std::function<int()> f = lam;
}


Related Topics



Leave a reply



Submit