2013-11-15 19 views
22

Quando si scrive una funzione in C++ che prende un timeout come uno dei suoi argomenti, che tipo dovrei usare per l'argomento di timeout stesso?Quale tipo usare per una variabile di timeout in C++?

Un esempio di una tale funzione potrebbe essere:

void my_function(bool work_really_hard, timeout_type timeout) 
{ 
// Do stuff, until timeout is reached. 
} 

ho pensato di usare std::chrono::seconds per timeout_type, ma che disabilita utilizzando qualsiasi timeout nel sub-secondo regno.

Quando si utilizza std::chrono::nanoseconds, invece, è ingombrante specificare, ad esempio, 10 minuti.

Un modo per risolvere questo in modo ragionevole, mantenendo la firma della funzione e le chiamate pulite e semplici?

+0

utilizzare il '' wait_until' o primitive sleep_until'? IOW, usa una scadenza invece di un timeout – sehe

+0

@sehe Gli interni di come il timeout è rispettato non sono rilevanti per questa domanda (non ho alcun controllo su di essi). Sono solo preoccupato di quale tipo utilizzare per il timeout nella firma della funzione. – JohnCand

+0

Come su millisecondi? È _kinda_ standard per specificare i timeout in millisecondi. Inoltre, i tipi 'std :: chrono' sono espressi in rapporti, quindi puoi usare valori non interi. – Tibor

risposta

24

Quando si utilizza std :: :: crono nanosecondi, invece, è ingombrante per specificare, ad esempio, 10 minuti.

In realtà, non lo rende ingombrante minimamente:

#include <chrono> 
#include <iostream> 

void my_function(bool work_really_hard, std::chrono::nanoseconds timeout) 
{ 
    std::cout << timeout.count() << '\n'; 
} 

int 
main() 
{ 
    my_function(true, std::chrono::minutes(10)); 
} 

uscita:

600000000000 

L'unica volta che avrai problemi con nanoseconds è se si vuole passare qualcosa che non si convertirà esattamente in nanoseconds, ad esempio picoseconds o duration<long, ratio<1, 3>> (unità di 1/3 di secondo).

Aggiornamento

ho inteso questa risposta per essere ulteriori informazioni per una risposta già accettato che ho pensato è stata una buona risposta (da sehe). sehe ha raccomandato una soluzione basata su modelli, che considero anche valida.

Se si desidera accettare qualsiasistd::chrono::duration, anche uno che potrebbe essere necessario truncate or round, poi andando con la risposta cancellato di sehe è la strada da percorrere:

template <typename Rep, typename Period> 
void my_function(bool work_really_hard, std::chrono::duration<Rep, Period> timeout) 
{ 
    // Do stuff, until timeout is reached. 
    std::this_thread::sleep_for(timeout); 
} 

Se per qualche motivo non si vuole trattare con modelli e/o si è soddisfatti di avere i clienti che devono specificare solo unità che sono esattamente convertibili in std::chrono:nanoseconds, quindi utilizzare std::chrono:nanoseconds come mostrato sopra è anche completamente accettabile.

Tutte le std::chrono unità "predefinite":

hours 
minutes 
seconds 
milliseconds 
microseconds 
nanoseconds 

sono implicitamente convertibile in nanoseconds, e non comporterà alcun troncamento o errore inerente. Il trabocco non si verificherà fintanto che lo si mantiene entro le due linee bianche luminose (riferimento oscuro per mantenere l'auto nella propria corsia).Finché la durata è entro +/- 292 anni, non devi preoccuparti di un eccesso con queste unità predefinite.

Le funzioni std-defined come std::this_thread::sleep_for sono rappresentate come se stesse suggerendo esattamente il motivo per cui si desidera essere interoperabili con ogni chrono:duration immaginabile (ad esempio 1/3 di un femtosecondo a virgola mobile). Spetta al progettista dell'API decidere se hanno bisogno di molta flessibilità nella propria API.

Se ora sono riuscito a confonderti invece di chiarire, non ti preoccupare troppo. Se si sceglie di utilizzare nanoseconds, le cose funzioneranno esattamente, senza errori di troncamento o di arrotondamento, oppure il client riceverà un errore in fase di compilazione. Ci sarà no errore di runtime.

void my_function(bool work_really_hard, std::chrono::nanoseconds timeout) 
{ 
    std::cout << timeout.count() << '\n'; 
} 

int 
main() 
{ 
    using namespace std; 
    using namespace std::chrono; 
    typedef duration<double, pico> picoseconds; 
    my_function(true, picoseconds(100000.25)); 
} 

test.cpp:15:9: error: no matching function for call to 'my_function' 
     my_function(true, picoseconds(100000.25)); 
     ^~~~~~~~~~~ 
test.cpp:4:10: note: candidate function not viable: no known conversion from 'duration<double, ratio<[...], 1000000000000>>' to 
     'duration<long long, ratio<[...], 1000000000>>' for 2nd argument 
    void my_function(bool work_really_hard, std::chrono::nanoseconds timeout) 
     ^
1 error generated. 

E se il cliente ottiene un errore di compilazione, si può sempre usare duration_cast per lavorare intorno ad esso:

my_function(true, duration_cast<nanoseconds>(picoseconds(100000.25))); // Ok 

Per ulteriori dettagli, vedere:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2661.htm

Aggiornamento dolce al codice originale nella parte superiore di questa risposta. In C++ 1A, che speriamo significa C++ 14:

using namespace std::literals; 
my_function(true, 10min); // also ok, is equal to 600000000000 nanoseconds 

Domanda: Cosa consiglia come un timeout di "infinito" (cioè non TIME OUT)

I prima tenterebbe di utilizzare un'API che non ha richiesto un timeout e che implicava "non timeout". Ad esempio condition_variable::wait. Se avessi il controllo dell'API, creerei tale firma senza un timeout.

In mancanza di questo, vorrei creare una serie di "grande" chrono::durations:

typedef std::chrono::duration 
< 
    std::int32_t, std::ratio_multiply<std::chrono::hours::period, std::ratio<24>> 
> days; 

typedef std::chrono::duration 
< 
    std::int32_t, std::ratio_multiply<days::period, std::ratio<7>> 
> weeks; 

typedef std::chrono::duration 
< 
    std::int32_t, std::ratio_multiply<days::period, std::ratio<146097, 400>> 
> years; 

typedef std::chrono::duration 
< 
    std::int32_t, std::ratio_divide<years::period, std::ratio<12>> 
> months; 

E poi vorrei usare uno di questi grandi durate nella mia chiamata, ad esempio:

std::this_thread::sleep_for(years(3)); 

I non proverei a push_things al massimo (potresti rompere le ipotesi sottostanti che il sistema operativo ha fatto per esempio). Scegli arbitrariamente qualcosa di ridicolmente grande (come 3 anni). Ciò attirerà l'attenzione del tuo revisore del codice e probabilmente darà inizio a una conversazione informativa. :-)

Ora disponibile in video: https://www.youtube.com/watch?v=P32hvk8b13M :-)

+0

+1 questa dovrebbe essere la risposta accettata. Questo ha molto senso ora sono imbarazzato :) – sehe

+0

Accetterò questa risposta e ammetto di essere imbarazzato per non aver provato qualcosa di così ovvio. 'std :: chrono :: duration' infatti [ha costruttori che accettano altri tipi di durata come argomenti] (http://en.cppreference.com/w/cpp/chrono/duration/duration). – JohnCand

+0

Vorrei poter revocare la tua risposta più di una volta, il tuo aggiornamento è molto apprezzato. Una piccola domanda di follow-up: cosa consiglieresti come timeout di "infinito" (cioè non timeout), se non voglio usare 0 per quello, tenendo anche conto di possibili conversioni/overflow firmati/non firmati problemi dei tipi sottostanti? – JohnCand

1

Utilizzare un parametro modello per il tipo di timeout e prevedere uno dei tipi std::chrono o qualcosa con un'interfaccia simile; questo ti permetterà di passare in qualsiasi unità di tempo.

template<typename T> 
void wait_a_while(const T& duration) 
{ 
    std::this_thread::sleep(duration); 
} 

wait_a_while(boost::posix_time::seconds(5)); 
wait_a_while(std::chrono::milliseconds(30)); 
+0

Solo un avviso, dal momento che sei arrivato quasi alla stessa risposta che ho fatto io. Cerca un momento di chiarezza :) – sehe

Problemi correlati