Search code examples
c++design-patternscallbackstatic-librariessignals-slots

Two way communication with (static) library


I am writing a program, the bulk of which is platform independent code compiled as a static library. I am then writing a program using any specific platform apis which is linked to the static library. Calling functions in the library from the actual program is obviously easy, however I am unsure of the best way (most efficient / cleanest implementation) of communicating with the program from the library.

I have looked into a signals / slots method however I am worried about the performance (ideally some draw calls would go through this). Otherwise the only other method I can think of is some form of callbacks using functors implementation, which should be quicker but is this the best design?

EDIT: My main aims / requirements is performance and easily implementable. The library is written in C++ and already uses boost so boost signals are a possibility, but is their performance acceptable?


Solution

  • Here are your options from (roughly) most flexible to least:

    Signals & slots

    Several signals and slots implementations are listed here (notably Boost.Signal). These are useful to implement the Observer design pattern where more than one object is interested in receiving notifications.

    Boost.Function

    You can register a boost::function callback. boost::function is wrapper around any callable entity: free function, static function, member function, or function object. To wrap a member function, you use boost::bind as shown in this example. Usage 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();
    }
    

    Fast Delegate

    There is a delegate implementation, called FastDelegate that is faster than boost::function. It uses an "ugly hack" that is not supported by the C++ standard, but is supported by practically all compilers.

    There is also The Impossibly Fast C++ Delegates that is supported by the standard, but not by all compilers.

    "Listener" interfaces (abstract classes)

    As c-smile suggested, you can register a pointer to an object derived from a callback interface (abstract class). This is the traditional Java way of doing callbacks. Example:

    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-style callbacks

    You register a pointer to a callback free function, plus an additional "context" void pointer. In the callback function, you cast the void* to the object type that will handle the event, and invoke the proper method. For example:

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

    Benchmarks

    I have found some performance benchmarks:

    They should give you an idea of what callback mechanism is appropriate.

    As you can see by the numbers, you have to be invoking Boost signals 10,000 to 100,000 of times per second before performance even becomes an issue.

    My Recommendation

    If callbacks will not be invoked at a blazingly high rate (10-100 thousand times per second), then use boost::signal for maximum flexibility and automatic connection lifetime management.

    If callbacks will be invoked at an extremely high rate, then start with boost::function for maximum flexibility and portability. It that's still too slow, then go with FastDelegate, or C-style callbacks.