Function Pointers





Item 14. Function Pointers

It's possible to declare a pointer to a function of a particular type.

void (*fp)(int); // ptr to function

Note the required use of parentheses to indicate that fp is a pointer to a function that returns void, not a function that returns void* (see Dealing with Function and Array Declarators [17, 61]). Like a pointer to data, a pointer to function may be null, or it may refer to a function of the appropriate type.

extern int f( int );
extern void g( long );
extern void h( int );
//...
fp = f; // error! &f is of type int(*)(int), not void(*)(int)   
fp = g; // error! &g is of type void(*)(long), not void(*)(int) 
fp = 0; // OK, set to null
fp = h; // OK, point to h
fp = &h; // OK, take address explicitly

Note that it is not necessary to take the address of a function explicitly when initializing or assigning its address to a pointer to function; the compiler knows implicitly to take the function's address, so using the & operator is optional in this case and is usually omitted.

In a similar fashion, it is not necessary to dereference a function pointer to call the function to which it refers, because the compiler will dereference it for you:

(*fp)(12); // explicit dereference
fp(12); // implicit dereference, same result

Note that no "generic" pointer exists that can point to any type of function the way a void* pointer can refer to any kind of data. Also note that the address of a non-static member function is not a pointer, so we can't point to a non-static member function with a function pointer (see Pointers to Member Functions Are Not Pointers [16, 57]).

One traditional use of function pointers is to implement callbacks (but see Function Objects [18, 63] and Commands and Hollywood [19, 67] for generally more effective callback techniques). A "callback" is a potential action that is set up in an initialization stage to be invoked in response to a future event. For example, if one were to catch on fire, it's best if one has planned out in advance how one should react:

extern void stopDropRoll();
inline void jumpIn() { ... }
//...
void (*fireAction)() = 0;
//...
if( !fatalist ) { // if you care that you're on fire...
    // then set an appropriate action, just in the event!
    if( nearWater )
        fireAction = jumpIn;
    else
        fireAction = stopDropRoll;
}

Once we've determined our course of action, a different part of our code can focus on if and when to execute the action, without being concerned with what the action is:

if( ftemp >= 451 ) { // if there's a fire...
    if( fireAction ) // ...and an action to execute...
        fireAction(); // ...execute it!
}

Note that it is legal to point to an inline function. However, calling an inline function through a function pointer will not result in an inline function call, because the compiler will not be able, in general, to determine at compile time precisely what function will be called. In our previous example, fireAction may point to either of two functions (or neither), so at the point of call the compiler has no choice but to generate code for an indirect, non-inline function call.

It's also legal to take the address of an overloaded function:

void jumpIn();
void jumpIn( bool canSwim );
//...
fireAction = jumpIn;

The type of the pointer is used to select among the various candidate functions. In this case, fireAction has type void(*)(), so the first jumpIn is selected.

Function pointers are used as callbacks in several places in the standard library, most notably by the standard set_new_handler function that sets a callback to be invoked if the global operator new function is unable to fulfill a memory request.

void begForgiveness() {
    logError( "Sorry!" );
    throw std::bad_alloc();
}
//...
std::new_handler oldHandler =
    std::set_new_handler(begForgiveness);

The standard typename new_handler is a typedef:

typedef void (*new_handler)();

The callback, therefore, must be a function that takes no argument and returns void. The set_new_handler function sets the callback to its argument and returns the previous callback; no separate functions exist for getting and setting. Simply getting the current callback requires some idiomatic gyrations:

std::new_handler current
    = std::set_new_handler( 0 ); // get...
std::set_new_handler( current ); // ...and restore!

The standard set_terminate and set_unexpected functions also use this combined get/set callback idiom.


     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows