传递回调成员函数的有效方法

时间:2010-06-23 00:11:35

标签: c++ performance boost callback

我正在为输入处理程序类设计一个方法。这是一些伪代码......

void InputHandler::ScanEvents( boost::function1< void, std::string& > &func ) {
   // Scan keys, determining string to pass
     // If string found, call func with string as its argument on object tied to func
}

我不确定如何实现它,或者如果它甚至可能,因为函数的重点是将它与调用者分开。这个想法是一个对象有一个私有成员函数和一个持有它的boost :: function成员。每当它在其InputHandler上调用ScanEvents时,它就会传递该函数,因此只要找到适当的事件,ScanEvents就可以“激活它”。

效率是一个问题,因为这是一个性能很重要的领域,而且这个功能经常被调用。

P.S。我发誓我记得在Scott Meyer的一本书中读过这样的例子,但我找不到它的生活。也许它出现在现代C ++设计......看起来......

4 个答案:

答案 0 :(得分:3)

boost::function的开销相当慢 - 约为20ns per call。我不会从内部循环调用它 - 但我也不会调用任何其他类型的函数指针。要调用成员函数,只需使用boost::lambda::bind构造匿名包装函数:

ih->ScanEvents(boost::lambda::bind(&ThisClass::CallbackFunc, this, boost::lambda::_1));

如果您需要真正高性能的一个选项是使用带有boost::lambda仿函数参数的inlinable模板函数:

template<typename F>
double democaller(const F &f) {
    double x = 1;
    for (int i = 0; i < 1000000; i++) {
        x = f(x);
    }
    return x;
}

namespace l = boost::lambda;

void demouser() {
    std::cout << democaller(l::_1 + 1);
}

在足够高的优化设置上,lambda表达式可以内联到调用站点,几乎完全消除了开销。

答案 1 :(得分:2)

的内容
class Thingy
{
public:
    Thingy() : callback(bind(&Thingy::Callback, this, _1)) {}

    void DoStuff()
    {
        handler.ScanEvents(callback);
    }

private:
    InputHandler handler;
    function<void(string)> callback;

    void Callback(string s)
    {
        cout << "Called with " << s << endl;
    }
};

应该做你所描述的。但是回调通过引用获取字符串可能会更有效。

答案 2 :(得分:1)

您可能正在寻找Modern C ++ Design中的命令实现。

boost :: function的开销与执行它所需的开销一样多。它的目的是允许您将“指针”传递给任何可以响应其接口的函数。如果您不需要此行为,则此开销是实际开销。如果你这样做,那么老实说,我看不到比boost :: function更好的方法(它甚至经过优化,以确保在某些实现中不会从虚拟函数中获得大量额外的内存使用)。

可以构造一个导致可能内联代码的方法,但是一旦你尝试将它存储在通用接口中,你就会得到boost :: function开销(可能更多)。

我建议使用boost :: function,直到你发现你真的需要用更快的东西替换它。那么,只有这样你才能编写不同复杂程度的模板来实现它。

简单地存储boost :: function可能足以满足您的需求。您还可以查看boost :: signals。

答案 3 :(得分:0)

通常,在另一个类上调用私有方法并不是最好的主意。

您可以实现一个接口样式的类并继承它,并编写您的ScanEvents方法以期望该类型的输入,例如:

class Notifyable {
  public:
    virtual void notify(std::string s) = 0;
};

void InputHandler::ScanEvents( Notifyable &n ) {
    // Scan keys, determining string to pass
      // If string found, call func with string as its argument on object tied to func
      n.notify(<string>);
}

我认为这也可能更快,只是因为你使用boost :: function包装器避免了任何可能的开销。