Sample function processing time decorator in c++11

2019-07-18 02:52发布

问题:

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.

回答1:

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)...);
}


回答2:

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;
 }