3

I want to make a function to test the running time of the incoming function. I use templates to make it applicable to many functions.

I omitted the code for counting time. like this:

template<typename F>
void get_run_time(F func)
{
//time start
 func;
//time end
}

But if a function I pass in is void, an error will be report and prompt me to add F=void. I tried to add it, but it didn't work. I can change void to bool, but it's very strange.

Another problem is that I want to test a function time and run my whole code normally .So I increased the return value. like this:

template<typename F>
F get_run_time(F func)
{
//time start
 F tf=func;
//time end
 return tf;
}

But the actual test time is obviously wrong. I guess it starts to run the function when it return .How can it get the running results before continuing with the following code?

JHBonarius
  • 10,824
  • 3
  • 22
  • 41
jerx
  • 77
  • 4

2 Answers2

3

The idiomatic C++ way (as I see it) is this

template <class F>
auto call(F f)
{
  Timer t;
  return f();
}

This works with functions returning void. Note, no start and stop here. Timer is a RAII class that starts the timer on construction and stops on destruction.

Forwarding parameters to f and niceties like std::invoke are not shown for brevity.

JHBonarius
  • 10,824
  • 3
  • 22
  • 41
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • 2
    I'm wondering, isn't this one of the few cases where `decltype(auto)` can/should be used as a return type, in order to perfectly forward it? – JHBonarius May 26 '22 at 07:10
  • @JHBonarius frankly I never understood it – n. m. could be an AI May 26 '22 at 07:29
  • 1
    @n.1.8e9-where's-my-sharem. with `auto` we invoke the type deduction, and with the `decltype` we introduce the `decltype` rules for the deduction. So, the `call` will return exactly the type that the `f` returns. – rawrex May 26 '22 at 07:34
  • 1
    @rawrex we also can use original `auto call(F f) -> type-id` syntax with `std::invoke_result_t` as type-id which would be invariant of call's code, not relying on the return statement – Swift - Friday Pie May 27 '22 at 14:16
2

First, you need to call the passed function to actually time its execution. Note, in your code you do not call it, using the () call operator:

template <typename Func>
void timer1 (Func func)
{
    // start timer
    func(); 
    // stop timer
}

Second, note these nuances:

// Will take the same time as the timer1
template <typename Func>
Func timer2 (Func func1)
{
    // func2 is a copy of func1
    // This copying won't increase the time as you thought it will
    Func func2 = func1;
    // You still need to call the function
    func2();

    // Returns pointer to function
    return func2;
}

void foo() { std::cout << "foo()" << std::endl; }

int main() {
    // Func, here, is a type void (*)()
    // Prints "foo()"
    timer2(foo);    
}

Third, you may want to look toward this approach:

// Since C++14
auto timer3 = [](auto&& func, auto&&... args)
{
    // start timer

    // Forward the func and invoke the call operator
    // with however many forwarded arguments passed to the timer3
    std::forward<decltype(func)>(func)(std::forward<decltype(args)>(args)...);

    // stop timer
};

void foo(int, int) {}

int main() 
{
    timer3(foo, 21, 42);    
}

Even more proper and concise way to do the job, as noted by the @JHBonarius, is to use the std::invoke (since C++17), which was covered in this thread: What is std::invoke in C++?

rawrex
  • 4,044
  • 2
  • 8
  • 24