2013-07-19 13 views
9

Precedentemente stavo usando una macro per misurare l'ora in cui una chiamata di funzione ha avuto luogo ogni volta che volevo controllare rapidamente. Ora, con il C++ 11 a disposizione, vorrei cancellare finalmente quel brutto pace del codice preprocessore e sostituirlo con qualcosa di simile:Inoltro perfetto per le funzioni di restituzione void e non vuoto

template <typename Functor, typename ... Args> 
auto measure(Functor f, Args && ... args) 
    -> decltype(f(std::forward<Args>(args)...)) 
{ 
    auto now = std::chrono::high_resolution_clock::now(); 
    auto ret = f(std::forward<Args>(args)...); 
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
     std::chrono::high_resolution_clock::now() - now).count(); 
    std::cout << "Time elapsed: " << elapsed << "ms" << std::endl; 

    return ret; 
} 

che funziona bene per le funzioni che restituiscono qualcosa (vale a dire non void). Quindi mi sentivo come se avessi bisogno di un sovraccarico per le funzioni void - ma non è possibile sovraccaricare una funzione solo sul tipo restituito.

Ho cercato di aggirare questo problema utilizzando alcuni modelli di magia, ma senza successo; il compilatore si lamenta ancora che la funzione measure è definita due volte:

template < 
    typename Functor, typename ... Args, 
    typename ReturnType = typename std::enable_if< 
     !std::is_void< 
      typename std::result_of<Functor(Args...)>::type 
     >::value, 
     typename std::result_of<Functor(Args...)>::type 
    >::type 
> 
ReturnType measure(Functor f, Args && ... args) 
{ 
    auto now = std::chrono::high_resolution_clock::now(); 
    auto ret = f(std::forward<Args>(args)...); 
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
     std::chrono::high_resolution_clock::now() - now).count(); 
    std::cout << "Time elapsed: " << elapsed << "ms" << std::endl; 

    return ret; 
} 

template < 
    typename Functor, typename ... Args, 
    typename ReturnType = typename std::enable_if< 
     std::is_void< 
      typename std::result_of<Functor(Args...)>::type 
     >::value 
    >::type 
> 
ReturnType measure(Functor f, Args && ... args) 
{ 
    auto now = std::chrono::high_resolution_clock::now(); 
    f(std::forward<Args>(args)...); 
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
     std::chrono::high_resolution_clock::now() - now).count(); 
    std::cout << "Time elapsed: " << elapsed << "ms" << std::endl; 
} 

Esiste un modo per aggirare questo?


UPDATE

Ecco la funzione ora sto usando grazie a R. Martinho Fernandes:

template <typename Functor, typename ... Args> 
auto measure(Functor f, Args && ... args) 
    -> decltype(f(std::forward<Args>(args)...)) 
{ 
    struct scoped_timer 
    { 
     scoped_timer() : now_(std::chrono::high_resolution_clock::now()) {} 
     ~scoped_timer() 
     { 
      auto elapsed = std::chrono::duration_cast< 
        std::chrono::milliseconds 
       >(std::chrono::high_resolution_clock::now() - now_).count(); 
      std::cout << "Time elapsed: " << elapsed << "ms" << std::endl; 
     } 

     private: 
      std::chrono::high_resolution_clock::time_point const now_; 
    } scoped_timer; 

    return f(std::forward<Args>(args)...); 
} 
+0

Vedere http://flamingdangerzone.com/cxx11/2012/06/01/almost-static-if.html#evolution –

+0

Elegante [idea] (http://stackoverflow.com/a/17748197/1137388) (di [R. Martinho Fernandes] (http://stackoverflow.com/users/46642/r-martinho-fernandes)). L'unica modifica che farei sarebbe posizionare il codice '~ scoped_timer()' in un blocco 'try-catch' che ingoia qualsiasi eccezione generata. Semanticamente, credo che abbia senso non riportare il tempo che 'f' impiega a correre se non si completa con successo. Sfortunatamente, questo non è così ovvio riguardo alle possibili eccezioni generate da << << '. Un vecchio 'printf' sarebbe un'alternativa migliore (per quanto riguarda la sicurezza delle eccezioni)? Non lo so. –

risposta

14

Il problema è che gli argomenti di template di default non fanno per i diversi modelli , allo stesso modo in cui gli argomenti delle funzioni di default non creano sovraccarichi diversi. Ci sono alcuni modi per aggirare questo e li ho descritti nel mio articolo Remastered enable_if.

Tuttavia, non lo farei. Vorrei semplicemente approfittare del fatto che nel codice generico è possibile "return vuoto", e utilizzare RAII per stampare il tempo trascorso:

template <typename Functor, typename ... Args> 
auto measure(Functor f, Args && ... args) 
    -> decltype(f(std::forward<Args>(args)...)) 
{ 
    scoped_timer timer; 
    return f(std::forward<Args>(args)...); 
} 

La classe scoped_timer può essere scritta banalmente: salvare now nel costruttore, e calcolare e produrre elapsed nel distruttore.

+0

In realtà, ho già fatto qualcosa di simile a questo, e ho semplicemente eliminato del tutto il valore del risultato, con 'measure' restituire invece il tempo trascorso. –

+0

Il tuo articolo è una lettura incredibilmente interessante. Grazie. Mi piacerebbe comunque restituire il valore originariamente restituito dalla funzione, ma la tua soluzione funziona bene per questo. – user2573221

Problemi correlati