I am trying to write a template that will take any function and log its time and after some struggle with template syntax i have come up with below solution:
template <typename Func, typename... Args>
auto timeMyFunction(Func f, Args... args)-> typename
std::enable_if<std::is_same<decltype(f(args...)),void>::value,void>::type
{
auto start = std::chrono::steady_clock::now();
f(args...);
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = end-start;
std::cout << "Time to complete function " << diff.count()<<std::endl;
}
template <typename Func, typename... Args>
auto timeMyFunction(Func f, Args... args)-> typename std::enable_if<!std::is_same<decltype(f(args...)),void>::value,decltype(f(args...))>::type
{
auto start = std::chrono::steady_clock::now();
auto ret = f(args...);
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = end-start;
std::cout << "Time to complete function " << diff.count()<<std::endl;
return ret;
}
But what i feel is this solution is some kind of Jugaad (https://en.wikipedia.org/wiki/Jugaad) and need your help in finding better solution. So what i am looking for is template that will calculate processing time of passed function and if possible can it be done without overloading as in above code i need to overload because i can't declare something like void ret
. Also is my usage of decltype
correct here.
Please suggest how it can be modified so that i can time class member and static function also via this template.
Wrap the time computation/display in a class:
class TimerDisplayer
{
public:
TimerDisplayer() {}
~TimerDisplayer()
{
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = end - start;
std::cout << "Time to complete function " << diff.count() << std::endl;
}
std::chrono::time_point<std::chrono::steady_clock> start =
std::chrono::steady_clock::now();
};
And then
template <typename Func, typename... Args>
decltype(auto) timeMyFunction(Func&& f, Args&&... args)
{
TimerDisplayer timerDisplay;
return std::forward<Func>(f)(std::forward<Args>(args)...);
}
Not a great solution but... you can create a template exec
struct with a method that return the value of f(args...)
, if the return type isn't void
, or a fake value (an int
, by example), otherwise.
By example
template <typename RetType>
struct execStruct
{
template <typename Func, typename ... Args>
static constexpr RetType exec (Func f, Args ... as)
{ return f(as...); }
};
template <>
struct execStruct<void>
{
template <typename Func, typename ... Args>
static constexpr int exec (Func f, Args ... as)
{ return (void)f(as...), 0; }
};
So you can detect the return type (RetType
, in the following example), save the value (fake value, in the void case) returned by execStruct::exec(f, args...)
and return the saved value casting it to retType
. So, if RetType
is void
, casting the fake value to void
is like writing return;
.
The following is a full working example
#include <chrono>
#include <utility>
#include <iostream>
#include <functional>
template <typename RetType>
struct execStruct
{
template <typename Func, typename ... Args>
static constexpr RetType exec (Func f, Args ... as)
{ return f(as...); }
};
template <>
struct execStruct<void>
{
template <typename Func, typename ... Args>
static constexpr int exec (Func f, Args ... as)
{ return (void)f(as...), 0; }
};
template <typename Func, typename ... Args>
auto timeMyFunc (Func f, Args ... args) -> decltype( f(args...) )
{
using RetType = decltype( f(args...) );
auto start = std::chrono::steady_clock::now();
auto ret = execStruct<RetType>::exec(f, args...);
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = end-start;
std::cout << "Time to complete function " << diff.count()<<std::endl;
return RetType(ret);
}
static int foo1 (int, int)
{ return 42; }
void foo2 (std::string const &)
{ }
struct foo3
{
static std::string bar1 (long)
{ return "abc"; }
long bar2 (std::string const &)
{ return 0L; }
};
int main ()
{
using namespace std::placeholders;
foo3 f3;
auto f3b2a = std::bind(&foo3::bar2, &f3, _1);
auto f3b2b
= std::function<long(foo3 &, std::string const &)>(&foo3::bar2);
using foo1Type = decltype(timeMyFunc(foo1, 1, 2));
using foo2Type = decltype(timeMyFunc(foo2, "baz1"));
using bar1Type = decltype(timeMyFunc(foo3::bar1, 3L));
using bar2aType = decltype(timeMyFunc(f3b2a, "baz2"));
using bar2bType = decltype(timeMyFunc(f3b2b, f3, "baz3"));
static_assert(std::is_same<foo1Type, int>::value, "!");
static_assert(std::is_same<foo2Type, void>::value, "!");
static_assert(std::is_same<bar1Type, std::string>::value, "!");
static_assert(std::is_same<bar2aType, long>::value, "!");
static_assert(std::is_same<bar2bType, long>::value, "!");
std::cout << "- " << timeMyFunc(foo1, 1, 2) << std::endl;
timeMyFunc(foo2, "baz");
std::cout << "- " << timeMyFunc(foo3::bar1, 3L) << std::endl;
std::cout << "- " << timeMyFunc(f3b2a, "baz2") << std::endl;
std::cout << "- " << timeMyFunc(f3b2b, f3, "baz3") << std::endl;
}