Question Remplir une promesse (peut-être nulle)


J'écris du code multithread et utilise promis / future pour appeler une fonction sur un thread différent et retourner son résultat. Pour simplifier, je supprimerai entièrement la partie de filetage:

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();
}

Cela fonctionne très bien pour toute fonction qui renvoie nonvoid. Et la déclaration de retour pour l'avenir fonctionne aussi pour void. Mais je ne peux pas remplir la promesse si f est un void fonction, car:

promise.set_value (f ()); // Erreur: utilisation invalide de l'expression vide

Y a-t-il un moyen astucieux de définir la valeur dans le void cas en ligne, ou dois-je juste écrire une fonction d'aide comme call_set_value(promise, f) qui a une surcharge pour std::promise<R> et std::promise<void>?


15
2017-12-17 19:31


origine


Réponses:


UNE promise n'est qu'un type de fournisseur de résultats asynchrone. Au lieu d'un promise vous pourriez utiliser un packaged_task qui enveloppe un objet appelable, semblable à un std::function sauf que l'invoquer rend le résultat disponible via un future (et bien sûr, il gère la différence entre void et résultats non nuls):

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. selon la norme actuelle, ce code aurait une course de données si task() et task.get_future() se produire sur des threads séparés (et votre original aussi en utilisant une promesse), vous devriez donc appeler get_future() avant de confier la tâche à l'autre thread. En pratique, il devrait être sûr sur les mises en œuvre réelles et il existe un problème de bibliothèque (LWG 2412) pour le rendre valide de toute façon.


13
2017-12-17 23:13



Oui. La surcharge de fonctions est la solution la plus propre:

set(promise, f);

puis mettre en œuvre set en tant que fonctions surchargées, comme:

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(); 
}

13
2017-12-17 19:36