Callbacks are a vital part of event-driven, decoupled programming. C++, being a strongly-typed object-orientated language, makes callbacks that much harder to implement than, say in C (non-object orientated) or Javasciprt (object orientated but NOT strongly typed).
NOTE: I have written an open-source C++ callback library called slotmachine-cpp, which you can download from GitHub here.
|Callee||A object which gets passes a callback function, and then calls (executes) it.|
|Method||A function that belongs to an object.|
|Signals||Term used for “events” in an event/listener system.|
|Slots||Term used for objects which listen to signals in an event/listener system. These are normally implemented with a callback system.|
The problem arises when you want to make a callback to a method. A method is a member function of an object. To call a method, you can’t just know the functions memory address and call it, you also have to know the object that the function belongs to. This means that for method callbacks in their most simple form, the callee has to know the type of the object the function belongs to. For example:
// Create a method that needs to be called
// Create a method which can call A::PleaseCallMe
// Note that the object A has to be known here! This creates undesired coupling!
PassACallbackToMe(void (A::* pleaseCallMe)())
// Call the callback function, this part is just the same as if in C
// Create objects of type A and B
// Pass a callback to A::PleaseCallMe() into B so that
// B can then call it
Using Static Methods Or Non-Member Functions (C-Style)
One way to implement callbacks in C++ is to use static methods or non-member functions. This is pretty much how you do it in C, and callbacks of the type void (*myFunctionPtr)() can be used. However, this has the following disadvantages:
- You have to create stand-along callback functions
- Member methods cannot be called directly (obvious). All of the preceding points relate to calling methods…
- If you want to then call member methods, the static function has to know about the object, requiring the object to be global
- Member methods have to be made public to be called from the function/static method, messing with what should really be public and private.
- All of the above hint at the fact that this is not really an OOP solution
The above bullet list should suggest that while easy, static methods or non-member function callbacks is not really an ideal solution. Luckily, there are better solutions (keep reading).
Functors are objects in C++ in where the () operator has been overloaded. The () operator gives a function-like feel to the operator, in where you can do:
functorObj myFunctorObj; // A functor object (the () operator is overloaded)
myFunctorObj(); // Using the object like a function
A Type Indepedent Method
The ideal callback can be passes around with the callee knowing nothing about the object it is calling. This allows for the creation of proper, decoupled, re-usable librares. With a bit of what may first seem like C++ black-magic, you can implement type agnostic callbacks in C++.
A key trick is that at some point you have to strip away the type to pass the object and method address from a type-knowing object to a type-agnostic object. This uses memcpy(). Sounds dangerous?
C++ Callback Libraries
||Apache License, Version 2.0||9/10||http://www.cpgf.org/|
||GNU Library General Public License||7/10||http://libsigc.sourceforge.net/|
Callbacks In C++ Using Template Functors is a great page explaining and analysing all the different ways for implementing callbacks in C++. This includes the functional model, single rooted hierarchy, parameterize the caller, callee mix-in, and functors (which they promote).
The Type-safe Callbacks In C++ library on the Code Project gives a great, complete callback library for C++ which allows callbacks with 0 to 5 input arguments.
Functors to Encapsulate C and C++ Function Pointers is a short and simple tutorial on using functors.
Posted: January 17th, 2014 at 3:36 pm
Last Updated on: June 11th, 2017 at 12:28 pm