CodePlea

Random thoughts on programming
08 Mar 2010

C++ Callback Comparisons


Today I'm going to do a quick comparison of C++ callback choices. I'll look at C++'s native support for callbacks, as well as Boost, and PlusCallback. I wrote PlusCallback, so I'm obviously quite biased, but I'm going to demonstrate with actual code and can hopefully make a compelling case anyway.

First, we'll define two functions to work with, one method and one plain free function. Both functions have the same signature: int return type, and one int parameter.

struct X {
    int Foo(int a);
};

int FreeFoo(int a);

Now, using C++'s native method pointers, we can construct a callback for the Foo method.

X x;

//The method pointer and object pointer are stored separately.
int (X::*callback)(int) = &X::Foo;
X* object = &x;

//Call x.foo(5). Note use of rare ->* operator.
(object->*callback)(5);

Of course, this callback pointer can only ever point to a method in the struct X. You can't re-point it to a method in another class, and you can't re-point it to a free function. This makes it worthless for any callback functionality because the caller would have to know what it's going to call at compile time. If this information is available at compile time, you obviously don't need callbacks. Other drawbacks include the need to store the function pointer and object pointer separately, a complex syntax, and almost no run-time flexibility.

As a side note, ->* is called the "Bind pointer to member by pointer" operator. And yes, you can overload it.


Boost provides function pointers that are oceans better.

X x;

//Setup callback for x.foo. Note complexity for simple task.
boost::function<int (int)> callback = std::bind1st(std::mem_fun(&X::Foo), &x);

callback(5); //Call x.foo(5);

//Change callback to free function.
callback = FreeFoo;

//Call FreeFunction(8).
callback(8);

//Boost doesn't support the < operator for function callbacks.
//assert(!(callback < callback));

boost::function isn't too bad. It can be reset to any object or free function at run-time. The drawbacks are a huge library dependency and a complicated syntax. Also, boost::function objects can't be sorted or stored in containers that require a sort order (like std::set and std::map).

Fun fact: the C++ preprocessor expands

#include <boost/function.hpp>

into over 30,000 lines of code.


PlusCallback supports all these features with a simple syntax.

X x;

//Setup callback for x.foo.
cb::Callback1<int, int> callback(&x, &X::Foo);

//Call x.foo(5).
callback(5);

//Change callback to free function.
callback = FreeFoo;

//Call FreeFunction(8).
callback(8);

//The < operator works fine.
//One can safely store these callbacks in a set or map container.
assert(!(callback < callback));

PlusCallback is contained in a single header file. It doesn't drag any dependencies into your project, and it doesn't need compiling. It uses the zlib license, and can be used in commercial applications freely.