Reputation: 51253
I want to move and call a boost::packaged_task inside a lambda.
However, I can't figure out an elegant solution.
e.g. This won't compile.
template<typename Func>
auto begin_invoke(Func&& func) -> boost::unique_future<decltype(func())> // noexcept
{
typedef boost::packaged_task<decltype(func())> task_type;
auto task = task_type(std::forward<Func>(func));
auto future = task.get_future();
execution_queue_.try_push([=]
{
try{task();}
catch(boost::task_already_started&){}
});
return std::move(future);
}
int _tmain(int argc, _TCHAR* argv[])
{
executor ex;
ex.begin_invoke([]{std::cout << "Hello world!";});
//error C3848: expression having type 'const boost::packaged_task<R>' would lose some const-volatile qualifiers in order to call 'void boost::packaged_task<R>::operator ()(void)'
// with
// [
// R=void
// ]
return 0;
}
My rather ugly solution:
struct task_adaptor_t
{
// copy-constructor acts as move constructor
task_adaptor_t(const task_adaptor_t& other) : task(std::move(other.task)){}
task_adaptor_t(task_type&& task) : task(std::move(task)){}
void operator()() const { task(); }
mutable task_type task;
} task_adaptor(std::move(task));
execution_queue_.try_push([=]
{
try{task_adaptor();}
catch(boost::task_already_started&){}
});
What is the "proper" way to move a packaged_task into a lambda which calls it?
Upvotes: 5
Views: 2069
Reputation: 28097
With a proper implementation of std::bind (or something equivalent with respect to move-enabled types) you should be able to combine bind and a C++0x lambda like this:
task_type task (std::forward<Func>(func));
auto future = task.get_future();
execution_queue_.try_push(std::bind([](task_type const& task)
{
try{task();}
catch(boost::task_already_started&){}
},std::move(task)));
return future;
btw: You don't need a std::move around future because future is a local object. As such, it's already subject to potential copy elision and if the compiler is not able to do that elision it has to move construct the return value from 'future'. The explicit use of std::move in this case may actually inhibit a copy/move elision.
Upvotes: 2
Reputation: 146940
There's a similar question up that I posted about moving into lambdas. C++0x doesn't have any move capture syntax. The only solution I could come up with was some kind of proxy function object.
template<typename T, typename F> class move_capture_proxy {
T t;
F f;
public:
move_capture_proxy(T&& a, F&& b)
: t(std::move(a)), f(std::move(b)) {}
auto operator()() -> decltype(f(std::move(b)) {
return f(std::move(b));
}
};
template<typename T, typename F> move_capture_proxy<T, F> make_move_proxy(T&& t, F&& f) {
return move_capture_proxy<T, F>(std::move(t), std::move(f));
}
execution_queue.try_push(make_move_proxy(std::move(task), [](decltype(task)&& ref) {
auto task = std::move(ref);
// use task
});
Note that I haven't actually tried this code, and it would become a lot nicer with variadic templates, but MSVC10 doesn't have them so I don't really know about them.
Upvotes: 2