skip to content | skip to sidebar

Callback

A tiny open source C++ library for object oriented callback functions (functors) for those days boost or libsigc++ is a bit too much. This library is also integrated in LASS. It is released under the GNU GPL license.

history

  • 2.2 – added a Python port of the source generator script, which is nice.
  • 2.1 – fixed the ugly resource leak, and we generate the headers by Perl now, wicked! =)
  • 2.0 – revamped code, kudos Dirk Gerrits.
  • 1.0 – original release as on flipCode.

overview

The use of this library is to treat function calls as first class objects, so you can pass them around to be called at a later time.

int fun(int a, int b) { return a + b; }
// ...
CallbackR2<int, int, int> cb(fun);
// ...
int result = cb(1, 2); // will be 3.

Of course, the above example could be done just as easy with plain old C function pointers. That’s true, but the advantage of this library will come forward when you want to call a method of a specific object instead. You can tell what method must be called, and on what object. Later, the caller can execute this without being aware this is a different case than above. There’s no way you can do this with plain old function pointers.

class Foo
{
    int m_;
    public:
    Foo(int m): m_(m) {}
    int bar(int a, b) const { return m * (a + b); }
};
// ...
Foo foo(4);
CallbackR2<int, int, int> cb2(&foo, &Foo::bar);
// ...
int result2 = cb2(1, 2); // will be 12

Both constant as non-constant member functions are supported as dispatchers.

It is also possible to bind a function with a little different signature, but of which the arguments can be implicit converted to the right type.

float spam(float a, float b) { return a / b; }
// ...
CallbackR2<int, int, int> cb3(spam);
// ...
int result3 = cb3(5, 2); // will be 3.

Callbacks can also be in an empty state. This is when the callback has no dispatcher, no function call is bounded to it. Callbacks will be empty when constructed by the default constructor, or when one of the arguments is a NULL pointer

CallbackR0<double> cb4;
assert(cb4.empty());

// NULL function pointer
CallbackR1<bool, int> cb5(0);
assert(cb5.empty());

// NULL ojbect pointer
CallbackR2<int, int, int> cb6(0, &Foo::bar);
assert(cb6.empty());

When executing empty callbacks, we have to distinguish two situations: callbacks without return value, and callbacks with return value. In the former case, there’s no danger. When the callback is executed, it has no dispatcher to call. Hence, it won’t do anything and it will immediately return. In the latter case, an exception will be thrown. When the callback is executed, it has to return a value. Since there’s no dispatcher to call that can return this value, we’re in troubles. bdg::CallbackEmptyCall which derives from std::logic_error will be thrown. It’s a logic error, since you can test if the callback is empty before calling it.

// Callback without return value
Callback2<int, int> cb7;
assert(cb7.empty());
cb7(4, "hello"); // OK to call, nothing will happen

// Callback with return value
CallbackR2<std::string, int, int> cb8;
assert(cb8.empty());
try
{
    // ERROR, an exception will be thrown.
    std::string result8 = cb8(12, 34);
}
catch(CallbackEmptyCall)
{
    // you'll end up here on an empty call.
}
std::string result8b = cb8 ? cb8(12, 34) : "bah"; // will be "bah".

We have callbacks in two flavours: with or without return values. Each with up to 10 arguments. If you need more arguments, you should open the perl script generate.pl and change the value of the variable $n to whatever number of arguments you need. If you execute this script, all headers will be generated. To execute the perl script, you need to have Perl installed. Most Linux boxes have Perl installed by default. On windows you can use ActivePerl.