Using Function Pointers for Callbacks

Problem

You plan to call some function func1, and at runtime you need it to invoke another function func2. For one reason or another, however, you cannot simply hardcode the name of func2 within func1. func2 may not be known definitively at compile time, or perhaps func1 belongs to a third-party API that you can't change and recompile. In either case, you need a callback function.

Solution

In the case of the functions above, declare func1 to take a pointer to a function, and pass it the address of func2 at runtime. Use a typedef to make the messy syntax easier to read and debug. Example 15-1 shows how to implement a callback function with a function pointer.

Example 15-1. A callback function

#include // An example of a callback function bool updateProgress(int pct) { std::cout << pct << "% complete... "; return(true); } // A typedef to make for easier reading typedef bool (*FuncPtrBoolInt)(int); // A function that runs for a while void longOperation(FuncPtrBoolInt f) { for (long l = 0; l < 100000000; l++) if (l % 10000000 == 0) f(l / 1000000); } int main( ) { longOperation(updateProgress); // ok }

 

Discussion

In a situation such as that shown in Example 15-1, a function pointer is a good idea if updateProgress and longOperation shouldn't know anything about each other. For example, a function that updates the progress by displaying it to the usereither in a user interface (UI) dialog box, in a console window, or somewhere elsedoes not care about the context in which it is invoked. Similarly, the longOperation function may be part of some data loading API that doesn't care whether it's invoked from a graphical UI, a console window, or by a background process.

The first thing you will want to do is determine what the signature of the function is you plan to call and create a typedef for it. typedef is your friend when it comes to function pointers, because their syntax is ugly. Consider how you would declare a function pointer variable f that contains the address of a function that takes a single integer argument and returns a boolean. It would look like this:

bool (*f)(int); // f is the variable name

One could argue, convincingly, that this is no big deal and that I'm just a whiner. But what if you want a vector of such function pointers?

vector vf;

Or an array of them?

bool (*af[10])(int);

Function pointers do not look like ordinary C++ variable declarations whose format is often a (qualified) type name followed by a variable name. This is why they can make for messy reading.

Thus, in Example 15-1, I used a typedef like this:

typedef bool (*FuncPtrBoolInt)(int);

Once that was out of the way, I was free to declare function pointers that have the signature of returning bool and accepting a single integer argument as I would any other sort of parameter, like so:

void longOperation(FuncPtrBoolInt f) { // ...

Now, all longOperation needs to do is call f like it would any function:

f (l/1000000);

In this way, f can be any function that accepts an integer argument and returns bool. Consider a caller of longOperation that doesn't care about the progress. It can pass in a function pointer of a no-op function:

bool whoCares(int i) {return(true);} //... longOperation(whoCares);

More importantly, which function to pass to longOperation can be determined dynamically at runtime.

Категории