Let’s clarify this process with an example. Suppose you want to design an estimate() function that estimates the amount of time necessary to write a given number of lines of code, and you want different programmers to use the function. Part of the code for estimate() will be the same for all users, but the function will allow each programmer to provide his or her own algorithm for estimating time. The mechanism for that will be to pass to estimate() the address of the particular algorithm function the programmer wants to use. To implement this plan, you need to be able to do the following:
• Obtain the address of a function.
• Declare a pointer to a function.
• Use a pointer to a function to invoke the function.
Obtaining the Address of a Function
Obtaining the address of a function is simple: You just use the function name without trailing parentheses. That is, if think() is a function, then think is the address of the function. To pass a function as an argument, you pass the function name. Be sure you distinguish between passing the
process(think); // passes address of think() to process()
thought(think()); // passes return value of think() to thought()
The process() call enables the process() function to invoke the think() function from within process(). The thought() call first invokes the think() function and then passes the return value of think() to the thought() function.
Declaring a Pointer to a Function
To declare pointers to a data type, the declaration has had to specify exactly to what type the pointer points. Similarly, a pointer to a function has to specify to what type of function the pointer points. This means the declaration should identify the function’s return type and the function’s signature (its argument list). That is, the declaration should provide the same information about a function that a function prototype does. For example, suppose Pam LeCoder has written a time-estimating function with the following prototype:
double pam(int); // prototype
Here’s what a declaration of an appropriate pointer type looks like:
double (*pf)(int); // pf points to a function that takes
// one int argument and that
// returns type double
Tip
In general, to declare a pointer to a particular kind of function, you can first write a prototype for a regular function of the desired kind and then replace the function name with an expression in the form (*pf). In this case, pf is a pointer to a function of that type.
The declaration requires the parentheses around *pf to provide the proper operator precedence. Parentheses have a higher precedence than the * operator, so *pf(int) means pf() is a function that returns a pointer, whereas (*pf)(int) means pf is a pointer to a function:
double (*pf)(int); // pf points to a function that returns double
double *pf(int); // pf() a function that returns a pointer-to-double
After you declare pf properly, you can assign to it the address of a matching function:
double pam(int);
double (*pf)(int);
pf = pam; // pf now points to the pam() function
Note that pam() has to match pf in both signature and return type. The compiler rejects nonmatching assignments:
double ned(double);
int ted(int);
double (*pf)(int);
pf = ned; // invalid -- mismatched signature
pf = ted; // invalid -- mismatched return types
Let’s return to the estimate() function mentioned earlier. Suppose you want to pass to it the number of lines of code to be written and the address of an estimating algorithm, such as the pam() function. It could have the following prototype:
void estimate(int lines, double (*pf)(int));
This declaration says the second argument is a pointer to a function that has an int argument and a double return value. To have estimate() use the pam() function, you pass pam()’s address to it:
estimate(50, pam); // function call telling estimate() to use pam()
Clearly, the tricky part about using pointers to functions is writing the prototypes, whereas passing the address is very simple.
Using a Pointer to Invoke a Function
Now we get to the final part of the technique, which is using a pointer to call the pointed-to function. The clue comes in the pointer declaration. There, recall, (*pf) plays the same role as a function name. Thus, all you have to do is use (*pf) as if it were a function name:
double pam(int);
double (*pf)(int);
pf = pam; // pf now points to the pam() function
double x = pam(4); // call pam() using the function name
double y = (*pf)(5); // call pam() using the pointer pf
Actually, C++ also allows you to use pf as if it were a function name:
double y = pf(5); // also call pam() using the pointer pf
Using the first form is uglier, but it provides a strong visual reminder that the code is using a function pointer.
History Versus Logic