Reputation: 302748
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
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
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