2013-07-19 30 views
9

Trước đây tôi đã sử dụng một macro để đo thời gian mà một cuộc gọi hàm đã thực hiện bất cứ khi nào tôi muốn kiểm tra nhanh. Bây giờ, với C++ 11 có sẵn, tôi muốn để cuối cùng loại bỏ rằng hòa bình xấu xí của mã tiền xử lý và thay thế bằng một cái gì đó như thế này:Chuyển tiếp hoàn hảo cho các chức năng trả về vô hiệu và không có hiệu lực

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

nào hoạt động tốt cho các chức năng mà trở về một cái gì đó (ví dụ: không void). Vì vậy, tôi cảm thấy như tôi cần một sự quá tải cho các chức năng void - nhưng bạn không thể quá tải một chức năng chỉ trên kiểu trả về.

Tôi đã cố gắng giải quyết vấn đề này bằng cách sử dụng một số mẫu ma thuật, nhưng vô ích; trình biên dịch vẫn than phiền rằng hàm measure được xác định hai lần:

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

Có cách nào khác không?


CẬP NHẬT

Dưới đây là chức năng bây giờ tôi đang sử dụng nhờ vào 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

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

+0

Thanh lịch [ý tưởng] (http://stackoverflow.com/a/17748197/1137388) (bởi [R. Martinho Fernandes] (http://stackoverflow.com/users/46642/r-martinho-fernandes)). Thay đổi duy nhất tôi sẽ thực hiện sẽ đặt mã '~ scoped_timer()' trong một khối 'try-catch' mà nuốt bất kỳ ngoại lệ nào. Về mặt ngữ nghĩa, tôi tin rằng nó không có nghĩa là không báo cáo thời gian 'f' cần chạy nếu nó không hoàn thành thành công. Thật không may, điều này không quá rõ ràng về các ngoại lệ có thể được đưa ra bởi '<<'. Liệu một 'printf' cũ có thể là một lựa chọn tốt hơn (liên quan đến an toàn ngoại lệ)? Tôi không biết. –

Trả lời

14

Vấn đề là đối số mẫu mặc định không làm cho các mẫu khác nhau , giống như cách các đối số hàm mặc định không thực hiện cho các tình trạng quá tải khác nhau. Có một số cách để giải quyết vấn đề này và tôi đã mô tả chúng trong bài viết Remastered enable_if của tôi.

Tuy nhiên, tôi sẽ không làm điều đó. Tôi chỉ đơn giản là sẽ tận dụng thực tế là trong mã chung bạn có thể "return khoảng trống", và sử dụng RAII để in ra thời gian trôi qua:

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

Lớp scoped_timer có thể được viết trivially: tiết kiệm now trong các nhà xây dựng, và tính toán và xuất ra elapsed trong trình hủy.

+0

Trên thực tế, tôi đã làm một cái gì đó tương tự như điều này trước đây, và tôi chỉ cần bỏ giá trị kết quả hoàn toàn, có 'measure' trả về thời gian trôi qua thay thế. –

+0

Bài viết của bạn là một bài đọc cực kỳ thú vị. Cảm ơn bạn. Tôi vẫn muốn trả lại giá trị hàm trả về ban đầu, nhưng giải pháp của bạn chỉ phục vụ tốt cho việc này. – user2573221

Các vấn đề liên quan