Двусторонняя связь со (статической) библиотекой - PullRequest
3 голосов
/ 21 июля 2011

Я пишу программу, большая часть которой является независимым от платформы кодом, скомпилированным как статическая библиотека. Затем я пишу программу, используя любую конкретную платформу API, которая связана со статической библиотекой. Вызывать функции из библиотеки из реальной программы, очевидно, легко, однако я не уверен в наилучшем способе (наиболее эффективной / чистой реализации) связи с программой из библиотеки.

Я рассмотрел метод сигналов / слотов, однако меня беспокоит производительность (в идеале некоторые вызовы отрисовки проходили бы через это). В противном случае единственный другой метод, о котором я могу подумать, - это некоторая форма обратных вызовов, использующих реализацию функторов, которая должна быть быстрее, но разве это лучший дизайн?

РЕДАКТИРОВАТЬ: Моими основными целями / требованиями является производительность и простота реализации. Библиотека написана на C ++ и уже использует повышение, так что сигналы повышения возможны, но приемлема ли их производительность?

Ответы [ 2 ]

4 голосов
/ 21 июля 2011

Вот ваши варианты от (примерно) наиболее гибкого до минимального:

Сигналы и слоты

Несколько реализаций сигналов и слотов перечислены здесь (в частности, Boost.Signal).Они полезны для реализации шаблона проектирования Observer , в котором более одного объекта заинтересованы в получении уведомлений.

Boost.Function

Вы можете зарегистрировать boost::function обратный звонок.boost::function - это обертка вокруг любой вызываемой сущности: свободная функция, статическая функция, функция-член или объект функции.Чтобы обернуть функцию-член, вы используете boost::bind, как показано в этом примере .Пример использования:

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

Fast Delegate

Существует реализация делегата, называемая FastDelegate , которая быстрее, чем boost::function.Он использует «некрасивый хак», который не поддерживается стандартом C ++, но поддерживается практически всеми компиляторами.

Также имеется Невозможно быстрые делегаты C ++ , которые поддерживаются стандартом, но не всеми компиляторами.

Интерфейсы "Listener" (абстрактные классы)

Как и предполагал 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();
}

Тесты

Я нашел несколько тестов производительности:

Они должны дать вам представление о том, какой механизм обратного вызова подходит.

Как видно по числам, вы должны вызывать сигналы Boost от 10 000 до 100 000 раз в секунду, прежде чем выполнять дажестановится проблемой.

Моя рекомендация

Если обратные вызовы не будут вызываться с невероятно высокой скоростью (10-100 тысяч раз в секунду), тогда используйте boost::signal для максимальной гибкости и автоматического подключенияуправление временем жизни.

Если обратные вызовы будут вызываться с очень высокой скоростью, начните с boost::function для максимальной гибкости и мобильности.Это все еще слишком медленно, затем используйте функцию обратного вызова FastDelegate или C-style.

1 голос
/ 21 июля 2011

Интерфейсы (абстрактные классы).Библиотека выставляет интерфейсы.И принимать интерфейсы обратного вызова для вызова из кода библиотеки.Проверенная временем классика.Зачем изобретать что-то еще?

...