C ++“简单”函数回调函数具有任意参数和返回值

时间:2012-03-09 13:25:24

标签: c++ callback compiler-errors profiling function-pointers

我正在尝试创建一个简单的函数timer / profiler。我的主要目标是创建一个函数,我可以快速,轻松,不引人注意地添加到我想要分析的任何函数调用。

因此,例如,对foobarbaz进行分析,我希望有一个ft(函数计时器)函数可以执行以下操作,从而影响原始函数代码尽可能少:

ft(foo());
ft(bar(1, 2, 3));
int result = ft(baz());
string result = ft(qux("a", 2, 3.4));

注意,对于bazquxft返回的结果应该是函数本身返回的结果。

ft处理所有时间和日志记录等。

我遇到过几个线程,提到如何为具有任意参数的函数执行函数回调,但不是很多,甚至提到如何处理返回值。

这是最接近的:Variable number of arguments (va_list) with a function callback?但是在尝试处理void函数以及返回值的函数时,我会被打结。

我开始认为使用宏是这样做的方法,但也许有更好的方法。

这是我目前的微弱尝试(删节):

static Timer fTimer;

class VoidFuncBase
{
public:
  virtual void operator()() = 0;
};

class VoidFuncWrapper0 : public VoidFuncBase
{
public:
  typedef void (*func)();
  VoidFuncWrapper0(func fp) : fp_(fp) { }
  void operator()() { fp_(); }
private:
  func fp_;
};

template<typename P1>
class VoidFuncWrapper1 : public VoidFuncBase
{
public:
  typedef void (*func)(const P1 &);
  VoidFuncWrapper1(func fp, const P1 &p1) : fp_(fp), p1_(p1) { }
  void operator()() { fp_(p1_); }
private:
  func fp_;
  P1 p1_;
};

template<typename P1, typename P2>
class VoidFuncWrapper2 : public VoidFuncBase
{
public:
  typedef void (*func)(const P1 &, const P2 &);
  VoidFuncWrapper2(func fp, const P1 &p1, const P2 &p2)
    : fp_(fp), p1_(p1), p2_(p2) { }
  void operator()() { fp_(p1_, p2_); }
private:
  func fp_;
  P1 p1_;
  P2 p2_;
};

template<typename R>
class FuncBase
{
public:
  virtual R operator()() = 0;
};

template<typename R>
class FuncWrapper0 : public FuncBase<R>
{
public:
  typedef R (*func)();
  FuncWrapper0(func fp) : fp_(fp) { }
  R operator()() { return fp_(); }
private:
  func fp_;
};

template<typename R, typename P1>
class FuncWrapper1 : public FuncBase<R>
{
public:
  typedef R (*func)(const P1 &);
  FuncWrapper1(func fp, const P1 &p1) : fp_(fp), p1_(p1) { }
  R operator()() { return fp_(p1_); }
private:
  func fp_;
  P1 p1_;
};

template<typename R, typename P1, typename P2>
class FuncWrapper2 : public FuncBase<R>
{
public:
  typedef R (*func)(const P1 &, const P2 &);
  FuncWrapper2(func fp, const P1 &p1, const P2 &p2)
    : fp_(fp), p1_(p1), p2_(p2) { }
  R operator()() { return fp_(p1_, p2_); }
private:
  func fp_;
  P1 p1_;
  P2 p2_;
};

template<typename R>
R ft(FuncBase<R> func, std::string functionName)
{
    unsigned long threadId = getThreadId();
    double startTimeMs = fTimer.getMilliseconds();

    R result = func();

    double duration = fTimer.getMilliseconds() - startTimeMs;
    logf("%u %s took %fms", threadId, functionName.c_str(), duration);

    return result;
}

void ft(VoidFuncBase func, std::string functionName, int logTimeoutMs)
{
    unsigned long threadId = getThreadId();
    double startTimeMs = timer.getMilliseconds();

    func();

    double duration = timer.getMilliseconds() - startTimeMs;
    logf("%u %s took %fms", threadId, functionName.c_str(), duration);
}

目前我正在

  

“错误:无法将参数'func'声明为抽象类型   'VoidFuncBase'”。

但无论如何,我可能会走错方向。

2 个答案:

答案 0 :(得分:3)

Variadic模板是要走的路!在任何情况下,您都需要稍微更改函数的调整方式:

template <typename R, typename... T, typename... A>
R ft(R (*f)(T...), A&&... a) {
    // do you business
    return f(std::forward<A>(a)...);
}

int r0 = ft(&baz);
int r1 = ft(&qax, a, b, c);

但是当函数重载时,事情会变得很有趣。请注意,调用之后的任何处理都会进入调用之前设置的对象的析构函数。

答案 1 :(得分:2)

Deitmar的答案是解决方案的95%,但这是我为了让它在我的案例中起作用所做的调整:

  1. 为了使用void返回类型执行支持函数,我需要为此案例添加特定模板。
  2. 我需要调用方法而不是纯函数,所以我需要调整函数的引用方式。
  3. 也许因为2我发现我需要传入正在调用该函数的对象。也许这实际上并不是必要的,但这是我能让它发挥作用的唯一方式。
  4. 我想在函数回调之后做一些工作,但在ft返回之前,所以对代码进行了一些简单的修改。
  5. 我需要传递每个调用的附加信息,即函数名称的字符串,以便我可以进行合理的日志记录。
  6. 这是工作代码(删节)。

    template <class C, typename R, typename... T, typename... A>
    R ft(C* obj, R (C::*func)(T...), std::string functionName, A&&... args)
    {
        double startTimeMs = fTimer.getMilliseconds();
    
        //extra pre-call work
    
        R result = (obj->*func)(std::forward<A>(args)...);
    
        //extra post-call work
    
        double duration = fTimer.getMilliseconds() - startTimeMs;
        logf("%s took %fms", functionName.c_str(), duration);
    
        return result;
    }
    
    template <class C, typename... T, typename... A>
    void ft(C* obj, void (C::*func)(T...), std::string functionName, A&&... args)
    {
        double startTimeMs = fTimer.getMilliseconds();
    
        //extra pre-call work
    
        (obj->*func)(std::forward<A>(args)...);
    
        //extra post-call work
    
        double duration = fTimer.getMilliseconds() - startTimeMs;
        logf("%s took %fms", functionName.c_str(), duration);
    }
    

    然后进行函数调用:

    对于具有签名this

    void Foo::bar(int arg);对象的方法
    ft(this, &Foo::bar, "Foo::bar", (3));
    

    当处理子类中的对象时,我必须检查涉及的对象类型。也许有一种通用的方法来做到这一点,但这不是它!:

    Shape* shape = getShape();
    double area = 0.0;
    if(shape->getType() == SQUARE)
    {
        area = ft((Square*)shape, &Square::getArea, "Square::getArea");
    }
    else if(shape->getType() == TRIANGLE)
    {
        area = ft((Triangle*)shape, &Triangle::getArea, "Triangle::getArea");
    
    }
    else if(shape->getType() == CIRCLE)
    {
        area = ft((Circle*)shape, &Circle::getArea, "Circle::getArea");
    }