2013-08-14 2 views
2

У меня есть класс для хранения одной функции, и когда ее вызываемый хранит время работы функции. И это хорошо на функции возврата типа void. Но когда я хочу получить возвращаемый тип сохраненной функции, у меня есть «значение void, которое не игнорируется, как должно быть». Я не могу специализировать шаблон, потому что типы возврата не являются ковариантными (насколько я понимаю).измеряет время выполнения любых функций и возвращает возвращаемое значение функции

Таким образом, следующий класс является плохим.

class TimeDurationOperation { 
public: 
    TimeDurationOperation(boost::function<void(void)> operation_) 
     : operation(operation_) { } 

    template <typename R> R operate() { 
     const boost::posix_time::ptime start = 
      boost::posix_time::microsec_clock::local_time(); 

     R return_value = operation(); 

     const boost::posix_time::ptime stop = 
      boost::posix_time::microsec_clock::local_time(); 
     elapsed = stop - start; 
     return return_value; 
    } 

    boost::posix_time::time_duration elapsed_time() const { 
     return elapsed; 
    } 

private: 
    boost::function<void(void)> operation; 
    boost::posix_time::time_duration elapsed; 
}; 

Рабочие версии работают функции():

void operate() { 
    const boost::posix_time::ptime start = 
     boost::posix_time::microsec_clock::local_time(); 

    operation(); 

    const boost::posix_time::ptime stop = 
     boost::posix_time::microsec_clock::local_time(); 
    elapsed = stop - start; 
} 

Я хочу назвать это так:

TimeDurationOperation tdo(boost::bind(detail::fun1, 2000)); 
tdo.operate(); 
std::cout << tdo.elapsed_time() << std::endl; 

и

TimeDurationOperation tdo2(boost::bind(detail::fun2, 500)); 
int r = tdo2.operate<int>(); 
std::cout << tdo2.elapsed_time() << " and returned: " << r << std::endl; 

Что ваши предложения?

+0

Используйте 'template typename ', а затем используйте 'T' вместо' ', возможно? Я не эксперт по шаблонам, поэтому я не уверен на 100%. –

+0

Вы имели в виду, что я должен был бы подстроить свой класс TimeDurationOperation подписи функции? Потому что это не сработает, так как я вызываю объект функции следующим образом: 'operation()'. – spiralfuzet

+0

Ну, это было мое предложение, да. Но если это не сработает, вам может понадобиться найти другой способ решить всю проблему ... –

ответ

0

Хорошо, поэтому этот пост немного стар, но я думаю, что было бы полезно отправить ответ.

Использование современного C++ (11/14) эта задача очень проста.

#include <chrono> 
#include <functional> 

template <typename Ret, class... Params> 
struct TimeDurationOperation { 
    explicit TimeDurationOperation(std::function<Ret(Params...)> operation) 
     : operation(operation) { } 

    template<class R = Ret> 
    typename std::enable_if<!std::is_void<R>::value, R>::type operate(Params... vals) { 
     auto start = std::chrono::system_clock::now(); 
     R return_value(operation(std::forward<Params>(vals)...)); 
     auto stop = std::chrono::system_clock::now(); 
     elapsed = stop - start; 
     return return_value; 
    } 

    template<class R = Ret> 
    typename std::enable_if<std::is_void<R>::value, void>::type operate(Params... vals) { 
     auto start = std::chrono::system_clock::now(); 
     operation(std::forward<Params>(vals)...); 
     auto stop = std::chrono::system_clock::now(); 
     elapsed = stop - start; 
    } 

    const std::chrono::duration<double>& elapsed_time() const { 
     return elapsed; 
    } 

private: 
    std::function<Ret(Params...)> operation; 
    std::chrono::duration<double> elapsed; 
}; 

Вы можете использовать это следующим образом:

int return_test_function(int i) { 
    std::this_thread::sleep_for(20ms); 
    return i * 2; 
} 

TimeDurationOperation<int, int> tdo(return_test_function); 
auto result = tdo.operate(21); 
auto elapsed = static_cast<double>(duration_cast<milliseconds(tdo.elapsed_time()).count()); 

С переменным числом шаблонов, которые можно представлять любой список параметров. С помощью std :: function вы можете представлять любую функцию. Простые функции (указатели) неявно преобразуются в этот тип. Реализация SFINAE с помощью std :: enable_if. Если выражение в enable_if false, функция не компилируется. Попытка использовать «неправильную» функцию с шаблоном приведет к ошибке компилятора (или предупреждению, в зависимости от компилятора). Вы можете измерить прошедшее время с помощью std :: chrono.

Примечание: это решение использует только стандарт. Никакой сторонней библиотеки (например, Boost) не требуется.

0

Для безопасности типа вам нужно, чтобы TimeDuration знал тип возврата. Кроме того, более полезно, если он знает параметры:

template<class signature> class TimeDurationOperation; 

template<class Ret, class...Params> 
class TimeDurationOperation<Ret(Params...) { 
public: 
    TimeDurationOperation(boost::function<Ret(Params...)> operation_) 
     : operation(operation_) { } 

    Ret operate(Params... vals) { 
     const boost::posix_time::ptime start = 
      boost::posix_time::microsec_clock::local_time(); 

     R return_value = operation(std::forward<Params>(vals)...); 

     const boost::posix_time::ptime stop = 
      boost::posix_time::microsec_clock::local_time(); 
     elapsed = stop - start; 
     return return_value; 
    } 

    boost::posix_time::time_duration elapsed_time() const { 
     return elapsed; 
    } 

private: 
    boost::function<Ret(Params...)> operation; 
    boost::posix_time::time_duration elapsed; 
}; 

Теперь, как актуальная проблема: Шаблон специализация является первой и наиболее очевидной вещью.

template<class...Params> 
class TimeDurationOperation<void(Params...) { 
public: 
    TimeDurationOperation(boost::function<void(Params...)> operation_) 
     : operation(operation_) { } 

    void operate(Params... vals) { 
     const boost::posix_time::ptime start = 
      boost::posix_time::microsec_clock::local_time(); 

     operation(std::forward<Params>(vals)...); 

     const boost::posix_time::ptime stop = 
      boost::posix_time::microsec_clock::local_time(); 
     elapsed = stop - start; 
    } 

    boost::posix_time::time_duration elapsed_time() const { 
     return elapsed; 
    } 

private: 
    boost::function<void(Params...)> operation; 
    boost::posix_time::time_duration elapsed; 
}; 

В качестве альтернативы (хотя я бы назвал это ужасной идеей) можно было бы злоупотреблять деструкторами.

struct RAII_timer { 
     RAII_timer(boost::posix_time::time_duration& dest) 
      : start(boost::posix_time::microsec_clock::local_time()) 
      , elapsed(&dest) 
      {} 
     RAII_timer(const RAII_timer& NO_COPIES) = delete; 
     RAII_timer& operator=(const RAII_timer& NO_COPIES) = delete; 
     ~RAII_timer() { 
      const boost::posix_time::ptime stop = 
       boost::posix_time::microsec_clock::local_time(); 
      *elapsed = stop - start; 
     } 

     boost::posix_time::ptime start; 
     boost::posix_time::time_duration* elapsed; 
    }; 
    Ret operate(Params... vals) { 
     RAII_timer timer(elapsed); 
     return operation(std::forward<Params>(vals)...); 
    } 
+0

Использование 'RAII_timer', кажется, является элегантным способом позаботиться о вычислении прошедшего времени. Почему вы считаете это ужасной идеей? –

+0

@RSahu: Потому что он используется как способ выполнения невидимого кода, в отличие от очистки ресурсов. Деструкторы должны использоваться для очистки, а не для выполнения нормального кода. –

+0

Хммм ... Это интересная перспектива. Я использовал их во многих местах, где конструктор рассматривается как «инициализатор», а деструктор рассматривается как «финализатор». –

Смежные вопросы