Pass any function by parameter
I'm pretty sure there's no single answer to what's best - but this is a small improvement i.m.o. since it's a bit more generic.
#include <chrono>
#include <iostream>
#include <string>
#include <type_traits>
// enable it for invocables with any type of arguments
template <class Func, class... Args,
std::enable_if_t<std::is_invocable_v<Func, Args...>, int> = 0>
decltype(auto) callFunctionPrintTime(std::string fnName, Func fn, Args&&... args)
{
std::cout << ">> Running " << fnName << " ... " << std::endl;
auto t1 = std::chrono::high_resolution_clock::now();
//Call to the target function by forwarding the arguments to it
decltype(auto) retVal = fn(std::forward<Args>(args)...);
auto t2 = std::chrono::high_resolution_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::cout << "Done ! (" << duration << " ms)" << std::endl;
return retVal;
}
Alternatively, if you don't plan on making overloads for non-invocables (which seems pretty obvious that you wont when I think about it) you can use static_assert
instead of SFINAE:
template <class Func, class... Args>
decltype(auto) callFunctionPrintTime(std::string fnName, Func fn, Args&&... args)
{
static_assert(std::is_invocable_v<Func, Args...>, "must be invocable");
//...
Test usage:
int& a_func(int i) {
static int rv = 0;
rv += i;
return rv;
}
int main() {
int& ref = callFunctionPrintTime("a_func 1", a_func, 10);
std::cout << ref << '\n'; // prints 10
ref += 20;
callFunctionPrintTime("a_func 2", a_func, 100);
std::cout << ref << '\n'; // prints 130 (10 + 20 + 100)
}
Or some of the alternatives for calling myFunction
:
std::function<unsigned long()> fn = []() { return myFunction(15, 100000); };
std::cout << callFunctionPrintTime("myFunction", fn);
std::cout << callFunctionPrintTime("myFunction",
[]() { return myFunction(15, 100000); });
std::cout << callFunctionPrintTime("myFunction", myFunction, 15, 100000);
Some useful links:
decltype(auto)
, std::enable_if_t
, std::is_invocable_v
, SFINAE
Main idea is correct. there are some details which might be improved:
template <typename Func, typename ... Ts>
decltype(auto) callFunctionPrintTime(std::string_view fnName, Func&& f, Ts&&... args) {
static_assert(std::is_invocable_v<Func&&, Ts&&...>); // Possibly SFINAE instead.
std::cout << ">> Running " << fnName << " ... " << std::endl;
struct Finally {
std::chrono::time_point<std::chrono::high_resolution_clock> t1 =
std::chrono::high_resolution_clock::now();
~Finally() {
auto t2 = std::chrono::high_resolution_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::cout << "Done ! (" << duration << " ms)" << std::endl;
}
} finally;
return std::invoke(std::forward<Func>(f), std::forward<Ts>(args)...);
}
Now:
- handles
void
return type (without specialization required). - Log also in case of exception (You can go further with
std::uncaught_exceptions
or try/catch block to dissociate exception from normal path). - handle any invocable with its parameters.
For automatic name, we have to rely on MACRO:
#define CallFunctionPrintTime(F, ...) callFunctionPrintTime(#F, F __VA_OPT__(,) __VA_ARGS__)
Demo