Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fulfill a (possibly-void) promise

Tags:

c++

c++11

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>?

like image 396
Barry Avatar asked Dec 17 '14 19:12

Barry


People also ask

What is promise c++?

A promise is an object that can store a value of type T to be retrieved by a future object (possibly in another thread), offering a synchronization point. On construction, promise objects are associated to a new shared state on which they can store either a value of type T or an exception derived from std::exception .

What function is used to set the return value in a future promise channel once the running thread has exited?

The answer is using std::future object. Every std::promise object has an associated std::future object, through which others can fetch the value set by promise.

How can you reuse STD promises?

To reuse promises, simply reassign them. it should be my_promise = std::promise<int>() not my_promise = std::my_promise<int>() .


1 Answers

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.

like image 112
Jonathan Wakely Avatar answered Oct 31 '22 10:10

Jonathan Wakely