与(静态)库的双向通信

时间:2011-07-21 15:50:36

标签: c++ design-patterns callback static-libraries signals-slots

我正在编写一个程序,其中大部分是作为静态库编译的独立于平台的代码。然后我使用链接到静态库的任何特定平台apis编写程序。从实际程序中调用库中的函数显然很容易,但我不确定从库中与程序通信的最佳方法(最有效/最干净的实现)。

我已经研究过信号/插槽方法但是我担心性能(理想情况下一些绘制调用会经历这个)。否则,我能想到的唯一其他方法是使用仿函数实现的某种形式的回调,这应该更快,但这是最好的设计吗?

编辑:我的主要目标/要求是性能和易于实施。该库是用C ++编写的,并且已经使用了boost,因此增强信号是可能的,但它们的性能是否可以接受?

2 个答案:

答案 0 :(得分:4)

以下是您从大致灵活到最不灵活的选择:

信号&槽

列出了几个信号和插槽实现here(特别是Boost.Signal)。这些对于实现Observer设计模式很有用,其中多个对象有兴趣接收通知。

来自Boost.Function

您可以注册boost::function回调。 boost::function是任何可调用实体的包装:自由函数,静态函数,成员函数或函数对象。要包装成员函数,请使用boost::bind,如example所示。用法示例:

#include <iostream>
#include <boost/function.hpp>
#include <boost/bind.hpp>

typedef boost::function<void (void)> MouseCallback;

class Mouse
{
public:
    void registerCallback(MouseCallback callback) {callback_ = callback;}

    void notifyClicked() {if (callback_) callback_();}

private:
    MouseCallback callback_;
};

class Foo
{
public:
    void mouseClicked() {std::cout << "Mouse clicked!";}
};

int main()
{
    Mouse mouse;
    Foo foo;
    mouse.registerCallback(boost::bind(&Foo::mouseClicked, &foo));
    mouse.notifyClicked();
}

快速代表

有一个名为FastDelegate的委托实现,其速度比boost::function快。它使用了C ++标准不支持的“丑陋黑客”,但实际上所有编译器都支持它。

标准也支持The Impossibly Fast C++ Delegates,但并非所有编译器都支持。

“监听器”接口(抽象类)

正如c-smile建议的那样,您可以注册一个指向从回调接口(抽象类)派生的对象的指针。这是传统的Java回调方式。例如:

class MouseInputListener
{
public:
    virtual void mouseClicked() = 0;
    virtual void mouseReleased() = 0;
};

class Mouse
{
public:
    Mouse() : listener_(0) {}
    void registerListener(MouseInputListener* listener) {listener_ = listener;}
    void notifyClicked() {if (listener_) listener_->mouseClicked();}
    void notifyReleased() {if (listener_) listener_->mouseReleased();}

private:
    MouseInputListener* listener_;
};

class Foo : public MouseInputListener
{
public:
    virtual void mouseClicked() {cout << "Mouse clicked!";}
    virtual void mouseReleased() {cout << "Mouse released!";}
};

C风格的回调

您注册了一个指向回调自由函数的指针,以及一个额外的“上下文”无效指针。在回调函数中,将void*强制转换为将处理事件的对象类型,并调用正确的方法。例如:

typedef void (*MouseCallback)(void* context); // Callback function pointer type

class Mouse
{
public:
    Mouse() : callback_(0), context_(0) {}

    void registerCallback(MouseCallback callback, void* context = 0)
        {callback_ = callback; context_ = context;}

    void notifyClicked() {if (callback_) callback_(context_);}

private:
    MouseCallback callback_;
    void* context_;
};

class Foo
{
public:
    void mouseClicked() {cout << "Mouse clicked!";}

    static void callback(void* context)
        {static_cast<Foo*>(context)->mouseClicked();}
};

int main()
{
    Mouse mouse;
    Foo foo;
    mouse.registerCallback(&Foo::callback, &foo);
    mouse.notifyClicked();
}

基准

我找到了一些性能基准:

他们应该让你知道什么是适当的回调机制。

正如您可以从数字中看到的那样,在性能成为问题之前,您必须每秒调用10,000到100,000次Boost信号。

我的建议

如果不以极高的速率(每秒10-100万次)调用回调,则使用boost::signal以获得最大的灵活性和自动连接生命周期管理。

如果以极高的速率调用回调,则从boost::function开始,以获得最大的灵活性和可移植性。它仍然太慢,然后使用FastDelegate或C风格的回调。

答案 1 :(得分:1)

接口(抽象类)。库公开接口。并接受从库代码调用的回调接口。时间证明经典。为什么要发明其他东西?