Шаблонная функция с произвольным числом аргументов - PullRequest
0 голосов
/ 13 сентября 2018

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

int sum(int a, int b){
    std::this_thread::sleep_for(std::chrono::milliseconds(5000));
    return a + b;
}

template<typename T>
std::pair<std::chrono::milliseconds, T> invoke_measure_time(std::function<T()> f){
    auto start = std::chrono::system_clock::now();
    auto f_result = f();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start);

    return std::make_pair(duration, f_result);
}

int main(){
    auto fun = std::bind(sum, 10, 20);
    auto emma = invoke_measure_time<int>(fun);
    std::cout << "time: " << emma.first.count() << ", result = " << emma.second << std::endl;
}

Работает нормально, результаты ожидаемые, но первая строка main вызывает головную боль. Как я могу изменить invoke_measure_time, чтобы принимать любую функцию, не только эту без аргументов? Я бы хотел, чтобы моя главная выглядела так:

int main(){
    auto emma = invoke_measure_time<int, int, int>(sum, 10, 20);
    std::cout << "time: " << emma.first.count() << ", result = " << emma.second << std::endl;
}

или что-то в этом роде. Как мне этого добиться?

Ответы [ 3 ]

0 голосов
/ 13 сентября 2018

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

template<typename Function, typename... Args>
auto invoke_measure_time(Function f, Args&&... args) {
    auto start = std::chrono::system_clock::now();
    auto f_result = f(std::forward<Args>(args)...);
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start);

    return std::make_pair(duration, f_result);
}

Принимает любую функцию, подобную типу, и пересылает аргументывы переходите к invoke_measure_time к нему.Он также имеет дополнительные преимущества в том, что он может называться как

int main(){
    auto emma = invoke_measure_time(sum, 10, 20);
    std::cout << "time: " << emma.first.count() << ", result = " << emma.second << std::endl;
}
0 голосов
/ 13 сентября 2018

Вы можете сделать следующее:

template<typename F, typename ... Ts>
auto invoke_measure_time(F&& f, Ts&&... args)
{
    auto start = std::chrono::system_clock::now();
    auto f_result = std::invoke(std::forward<F>(f), std::forward<Ts>(args)...);
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start);

    return std::make_pair(duration, f_result);
}

* 1004 Demo *

Случай, когда f возврат void проблематичен.

0 голосов
/ 13 сентября 2018

Простое решение - заставить invoke_measure_time взять любой функциональный объект, который может быть вызван без параметров (для краткости я опускаю временные параметры):

template <typename F>
auto invoke_and_measure(F f) -> decltype(f()) {
    return f();
}

И затем любая функция,с произвольными параметрами, например, ваш sum:

int sum(int a,int b) { return a+b; }

может быть заключен в лямбду, которую можно вызывать без параметров:

int main() {
    auto x = invoke_and_measure([](){ return sum(1,2);});
    std::cout << x;
}

Живой пример

Обратите внимание, что с лямбдами обычно легче работать, чем с bind.Есть некоторые угловые случаи, которые может bind делать, но не лямбда, но на самом деле я ничего не знаю по макушке;)

...