2013-07-19 3 views
9

Раньше я использовал макрос, чтобы измерить время вызова функции, когда я захотел быстро проверить это. Теперь, с C++ 11 доступны, я хотел бы, наконец, удалить этот уродливый мир препроцессора кода и заменить его чем-то вроде этого:Превосходная пересылка для функций void и non-void

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

, который работает отлично подходит для функций, которые возвращают что-то (т.е. не void). Поэтому мне показалось, что мне нужна перегрузка для функций void, но вы не можете перегружать функцию только по типу возврата.

Я попытался обойти эту проблему, используя магию шаблонов, но безрезультатно; компилятор все еще жалуется, что функция measure определяется два раза:

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

Есть ли способ обойти это?


UPDATE

Вот функция Я сейчас, используя благодаря Р. 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

См. Http://flamingdangerzone.com/cxx11/2012/06/01/almost-static-if.html#evolution –

+0

Элегантный [идея] (http://stackoverflow.com/a/17748197/1137388) ([R. Martinho Fernandes] (http://stackoverflow.com/users/46642/r-martinho-fernandes)). Единственное изменение, которое я сделал бы, - это разместить код '~ scoped_timer()' в блоке 'try-catch', который проглатывает любое вызванное исключение. Семантически, я считаю, что имеет смысл не сообщать о времени, которое 'f' берет для запуска, если оно не завершено успешно. К сожалению, это не так очевидно в отношении возможных исключений, вызванных '<<'. Может ли старая «printf» стать лучшей альтернативой (в отношении безопасности исключений)? Я не знаю. –

ответ

14

Проблема заключается в том, что аргументы шаблона по умолчанию не делают для различных шаблонов , так же, как и аргументы функции по умолчанию для разных перегрузок. Есть несколько способов обойти это, и я описал их в моей статье Remastered enable_if.

Однако я бы этого не сделал. Я бы просто воспользоваться тем, что в родовом коде вы можете «return пустота», и использовать RAII распечатать истекшее время:

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

scoped_timer класс можно записать тривиальным: сохранить now в конструкторе, и вычислить и вывести elapsed в деструктор.

+0

На самом деле, я уже делал что-то похожее на это раньше, и я просто опустил значение результата в целом, вместо этого, вместо того, чтобы «измерить» вернуть прошедшее время. –

+0

Ваша статья невероятно интересна. Спасибо. Я хотел бы вернуть значение, возвращаемое функцией, но ваше решение отлично подходит для этого. – user2573221