2014-12-17 14 views
14

Sto scrivendo del codice multithreaded e utilizzo di promise/future per chiamare una funzione su un thread diverso e restituirne il risultato. Per simplicitly, mi tolgo la parte filettatura del tutto:Soddisfare una promessa (possibilmente nulla)

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

Questa grande opera per qualsiasi funzione che restituisce non void. E la dichiarazione di ritorno per il futuro funziona anche per void. Ma non posso mantenere la promessa, se f è una funzione di void, perché:

promise.set_value (f()); // errore: utilizzo non valido di espressione vuoto

C'è un modo intelligente di impostare il valore nel caso void in linea, o devo scrivere solo una funzione di supporto come call_set_value(promise, f) che ha un overload per std::promise<R> e std::promise<void>?

+0

Non ho le abilità di modello per farlo da solo, ma ho la sensazione che probabilmente si potrebbe SFINAE sul tipo di ritorno. – Borgleader

+2

Che cosa è esattamente il punto di chiamare la funzione su un altro thread se stai solo aspettando che la chiamata sia terminata? (Escluso il caso ovvio di inviare la chiamata a un thread * specifico *) – cdhowie

+4

Perché stai usando la promessa di basso livello - potresti semplicemente usare std :: future –

risposta

11

Un promise è solo un tipo di erogatori risultato asincrono. Invece di un promise si potrebbe usare un packaged_task che avvolge un oggetto callable, simile ad un std::function tranne che invoca rende i risultati disponibili attraverso un future (e, naturalmente, gestisce la differenza tra void ed i risultati non nullo):

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

NB in base allo standard corrente, questo codice avrebbe una corsa di dati se task() e task.get_future() si verificano su thread separati (e quindi il tuo originale usando una promessa), quindi dovresti chiamare get_future() prima di passare l'attività all'altro thread. In pratica dovrebbe essere sicuro sulle implementazioni reali e c'è un problema di libreria (LWG 2412) per renderlo comunque valido.

+0

Perché non posso spostare un'attività in 'std :: function'? per esempio. 'std :: function func = [t = std :: move (task)]() mutable {t(); }; 'fornisce un errore sul fatto che' task' non è copiabile. – Barry

+1

'std :: function' è copiabile, quindi richiede che il suo oggetto target sia anche copiabile (il tipo di destinazione è cancellato dal tipo, quindi' function' non può essere reso copiabile solo quando avvolge un oggetto copiabile, perché il target può cambiare dinamicamente in fase di esecuzione). 'packaged_task' è solo movimento, quindi non può essere memorizzato in una' funzione' –

+0

Oh, giusto! Freddo. Mi piace questa soluzione. Inoltre sono contento che tu abbia cambiato il tuo cappello da quello di Spock, perché sicuramente hai reso la tua foto con un solo occhio ancora più inquietante :) – Barry

12

Sì. sovraccarico funzione è la soluzione più pulita:

set(promise, f); 

poi attuare set come funzioni sovraccaricate, come:

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