c ++ 11中的示例函数处理时间装饰器

时间:2017-09-08 00:57:31

标签: c++ c++11 variadic-templates sfinae chrono

我正在尝试编写一个模板,该模板将采用任何函数并记录其时间,并且在使用模板语法进行了一些努力之后,我提出了以下解决方案:

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

但我觉得这个解决方案是某种Jugaad(https://en.wikipedia.org/wiki/Jugaad),需要你的帮助才能找到更好的解决方案。所以我正在寻找的是计算传递函数的处理时间的模板,如果可能的话可以在没有重载的情况下完成,因为我需要重载,因为我不能声明像void ret这样的东西。我在这里使用decltype也是正确的。

请建议如何修改它,以便我也可以通过此模板计时类成员和静态功能。

2 个答案:

答案 0 :(得分:4)

将时间计算/显示包装在一个类中:

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

然后

template <typename Func, typename... Args>
decltype(auto) timeMyFunction(Func&& f, Args&&... args)
{
    TimerDisplayer timerDisplay;
    return std::forward<Func>(f)(std::forward<Args>(args)...);
}

答案 1 :(得分:1)

不是一个很好的解决方案,但是......如果返回类型不是exec,您可以使用返回值f(args...)的方法创建模板void结构,或者一个假值(例如int),否则。

以示例

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

因此,您可以检测返回类型(RetType,在以下示例中),保存execStruct::exec(f, args...)返回的值(伪值,在void情况下)并返回保存的值,将其转换为retType。因此,如果RetTypevoid,则将假值转换为void就像撰写return;一样。

以下是一个完整的工作示例

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