Barry
Barry

Reputation: 302748

Fulfill a (possibly-void) promise

I'm writing some multithreaded code and using promise/future to call a function on a different thread and return its result. For simplicitly, I'll remove the threading part entirely:

template <typename F>
auto blockingCall(F f) -> decltype(f()) 
{
    std::promise<decltype(f())> promise;

    // fulfill the promise
    promise.set_value(f());

    // block until we have a result
    return promise.get_future().get();
}

This works great for any function that returns non-void. And the return statement getting the future also works for void. But I can't fulfill the promise if f is a void function, because:

promise.set_value(f()); // error: invalid use of void expression

Is there some clever way of setting the value in the void case in-line, or do I have to just write a helper function like call_set_value(promise, f) that has an overloads for std::promise<R> and std::promise<void>?

Upvotes: 15

Views: 4229

Answers (2)

Jonathan Wakely
Jonathan Wakely

Reputation: 171263

A promise is only one kind of asynchronous result provider. Instead of a promise you could use a packaged_task which wraps a callable object, similar to a std::function except that invoking it makes the result available via a future (and of course it handles the difference between void and non-void results):

template <typename F>
auto blockingCall(F f) -> decltype(f()) 
{
    std::packaged_task<decltype(f())()> task(std::move(f));

    task();

    // block until we have a result
    return task.get_future().get();
}

N.B. according to the current standard, this code would have a data race if task() and task.get_future() happen on separate threads (and so would your original using a promise), so you should call get_future() before handing the task to the other thread. In practice it should be safe on real implementations and there's a library issue (LWG 2412) to make it valid anyway.

Upvotes: 14

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361302

Yes. Function overload is the cleanest solution:

set(promise, f);

then implement set as overloaded functions, as:

template<typename F, typename R>
void set(std::promise<R> & p, F && f) //handle non-void here
{
    p.set_value(f()); 
}

template<typename F>
void set(std::promise<void> & p, F && f)  //handle void here
{
    f();
    p.set_value(); 
}

Upvotes: 14

Related Questions