Tuesday, May 7, 2013

Callback Mechanism in C++

Callbacks

Callback is a routine which is passed to some other code and which is expected to get called at some point of time. This will be very useful in sending notifications. It could also help in decoupling components. A framework for event notifications using callbacks will be very useful when designing complex systems.

To make a generic implementation, a callback should be able to take any number of inputs with varying  types. It could either be a simple non-member function, static function or a class member function.
C++11  provides an easy way of using callback using std::function.


std::function

std::function is introduced in C++11 and can be accessed after including header file functional .
It can store any callable target including static or non-member functions, member functions, lambda functions.
Let's see an example

#include <functional>
#include <iostream>

class Handler
{
    void invoke(int i, int j)
    {
        std::cout<<"Handler invoked with "<<i<<":"<<j<<"\n";
    }
};


int main(int argc, char* argv)
{
    std::function<void(Handler*, int, int)> 
        callback(&Handler::invoke);
    Handler h;
    callback(&h, 10, 20);
    
    return 0;
}

std::bind

Consider a scenario where some of the arguments of the callbacks need to be constant.
For eg:
Consider a function display which takes three parameters: Polygon, width and height.
Suppose we want to create a callback for this function, such that width and height should always be 100 and 200.
Client should only be able to invoke the callback with Polygon object. Width and Height should be set automatically.
This can be achieved by binding caller object and its parameters while initializing the callback.

Polygon p;
auto disp = std::bind(&display, std::placeholders::_1, 100, 200);
disp(p);

We have 29 such placeholders defined in the functional header.

std::bind creates a callback by binding the object and method(in case of member method) together with the arguments passed. If we do not know the arguments, we can specify placeholders.
While invoking the callback, we just need to pass the values of the placeholders.

Consider the first example again.
There we have passed pointer to object as the first argument.
What if we only wants to make a callback to member method of the same object?(we don't want to invoke the callback with a different object)
How to get rid of passing object pointer every time we invoke the same callback?
We can use the same approach using std::bind to achieve it.

auto callback2 = std::bind(&Handler::invoke, &h, std::placeholders::_1, std::placeholders::_2);
callback2(20, 30);

This will, ofcourse, make the callback2 bound to the same object.

std::function along with std::bind provides an elegant way of invoking callbacks in C++.