5

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?

ronag
  • 49,529
  • 25
  • 126
  • 221

2 Answers2

2

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.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • I actually added a solution involving std::bind to your question: http://stackoverflow.com/questions/4433109/ – sellibitze Dec 20 '10 at 20:30
  • Also, it seems you forgot about the template argument deduction rules that make perfect forwarding work. A couple of std::moves should be replaced with std::forward and a decay::type respectivly decay::type wouldn't hurt. I hope you are aware of the fact that T and F might be deduced to be an lvalue reference. – sellibitze Dec 20 '10 at 21:05
2

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.

sellibitze
  • 27,611
  • 3
  • 75
  • 95