2013-07-19 12 views
9

Früher habe ich ein Makro verwendet, um die Zeit zu messen, die ein Funktionsaufruf dauerte, wann immer ich das schnell überprüfen wollte. Jetzt, mit C++ 11 verfügbar, möchte ich endlich, dass hässliche Frieden Präprozessor Code entfernen und ersetzen sie durch etwas wie folgt aus:Perfekte Weiterleitung für ungültige und ungültige Funktionen

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

, die für Funktionen einwandfrei funktioniert, die etwas zurückgeben (das heißt nicht void). So fühlte ich mich wie eine Überlastung für void Funktionen benötigt - aber Sie können eine Funktion nicht nur auf Rückgabetyp überladen.

Ich habe versucht, dieses Problem mit einigen Vorlagen Magie zu umgehen, aber ohne Erfolg; noch der Compiler beschwert sich, dass die Funktion measure zweimal definiert:

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

Gibt es eine Möglichkeit, um dieses?


UPDATE

Hier ist die Funktion, die ich jetzt bin mit dank 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

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

+0

Elegant [Idee] (http://stackoverflow.com/a/17748197/1137388) (von [R. Martinho Fernandes] (http://stackoverflow.com/users/46642/r-martinho- fernandes)). Die einzige Änderung, die ich machen würde, würde '' scoped_timer() 's Code in einen' try-catch'-Block setzen, der jede ausgelöste Ausnahme verschluckt. Semantisch glaube ich, dass es sinnvoll ist, nicht die Zeit anzugeben, die "f" benötigt, um ausgeführt zu werden, wenn sie nicht erfolgreich abgeschlossen wird. Leider ist dies nicht so offensichtlich in Bezug auf mögliche Ausnahmen, die von "<<" ausgelöst werden. Wäre ein alter 'printf' eine bessere Alternative (bezüglich der Ausnahmesicherheit)? Ich weiß es nicht. –

Antwort

14

Das Problem ist, dass Standardvorlage Argumente machen nicht für verschiedene Vorlagen , genauso wie Standardfunktionsargumente nicht für verschiedene Überladungen sorgen. Es gibt einige Möglichkeiten, und ich beschrieb sie in meinem Remastered enable_if Artikel.

Allerdings würde ich das nicht tun. Ich würde einfach die Vorteile der Tatsache, dass in einem generischen Code können Sie „return Leere“ und RAII verwenden aus der verstrichenen Zeit zu drucken:

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

Die scoped_timer Klasse trivialer geschrieben werden kann: now im Konstruktor speichern, und berechnen und elapsed im Destruktor ausgeben.

+0

Eigentlich habe ich etwas Ähnliches vorher gemacht, und ich habe den Ergebniswert einfach fallen lassen, nachdem 'measure' stattdessen die verstrichene Zeit zurückgegeben hat. –

+0

Ihr Artikel ist eine unglaublich interessante Lektüre. Vielen Dank. Ich möchte immer noch den Wert zurückgeben, den die Funktion ursprünglich zurückgibt, aber Ihre Lösung ist dafür geeignet. – user2573221