Выполнение (возможно, недействительного) обещания
Я пишу многопоточный код и используя обещание/будущее, чтобы вызвать функцию в другом потоке и вернуть ее результат. Для упрощения я полностью удаляю резьбовую часть:
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();
}
Это отлично работает для любой функции, которая возвращает не void
. И выражение return, получающее будущее, также работает для void
. Но я не могу выполнить обещание, если f
является функцией void
, потому что:
promise.set_value (е());// ошибка: недопустимое использование выражения void
Есть ли какой-нибудь умный способ установки значения в случае void
в строке, или мне нужно просто написать вспомогательную функцию типа call_set_value(promise, f)
, которая имеет перегрузки для std::promise<R>
и std::promise<void>
?
Ответы
Ответ 1
A promise
- это только один асинхронный поставщик результатов. Вместо promise
вы можете использовать packaged_task
, который обертывает вызываемый объект, похожий на std::function
, за исключением того, что его вызывает доступ к результату через future
(и, конечно же, он обрабатывает разницу между void
и непустые результаты):
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. в соответствии с действующим стандартом, этот код будет иметь гонку данных, если task()
и task.get_future()
произойдут в отдельных потоках (и ваш оригинал будет использовать обещание), поэтому вы должны позвонить get_future()
, прежде чем передать задачу другому нить. На практике это должно быть безопасно для реальных реализаций, и там проблема с библиотекой (LWG 2412), чтобы сделать ее действительной в любом случае.
Ответ 2
Да. Функция перегрузки - самое чистое решение:
set(promise, f);
затем реализуем set
как перегруженные функции, так как:
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();
}