2.8 Pointers to Functions Add Flexibility
We must provide a function to return a vector of elements similar to fibon_seq() for each of our other five numerical sequences. The full set of functions might be declared as follows:
const vector<int> *fibon_seq( int size );
const vector<int> *lucas_seq( int size );
const vector<int> *pell_seq( int size );
const vector<int> *triang_seq( int size );
const vector<int> *square_seq( int size );
const vector<int> *pent_seq( int size );
What about fibon_elem()? Must we also provide six separate instances of this, one for each numeric sequence? The definition of fibon_elem() is as follows:
bool fibon_elem( int pos, int &elem )
{
const vector<int> *pseq = fibon_seq( pos );
if ( ! pseq )
{ elem = 0; return false; }
elem = (*pseq)[ pos-1 ];
return true;
}
The only sequence-dependent aspect of fibon_elem() is the call to the associated sequence function to retrieve the vector of elements. If we eliminate this dependency, we eliminate the need for more than a single instance of the function. We achieve this independence using a pointer ?specifically,
a pointer to function.
The definition of a pointer to function is complicated. It must specify the return type and parameter list of the function it is addressing. In our case, the parameter list is a single int, and the return type is const vector<int>*. In addition, the definition must place a * somewhere to indicate that the object being defined is a pointer. Finally, of course, we must give the pointer a name. Let's call it seq_ptr. As usual, our first attempt is almost correct.
const vector<int>* *seq_ptr( int ); // almost correct
This code defines seq_ptr as a function that has a parameter list of a single int and has a return type of a pointer to a pointer to a const vector of elements of type int! To have seq_ptr be recognized as a pointer, we must override the default precedence of * with parentheses:
const vector<int>* (*seq_ptr)( int ); // ok
seq_ptr can address any function with the same return type and parameter list. This means that it can address each of the six numeric sequence functions. Let's rewrite fibon_elem() as the more general seq_elem() as follows:
bool seq_elem( int pos, int &elem,
const vector<int>* (*seq_ptr)(int))
{
// invoke function addressed by seq_ptr
const vector<int> *pseq = seq_ptr( pos );
if ( ! pseq )
{ elem = 0; return false; }
elem = (*pseq)[ pos-1 ];
return true;
}
The function addressed by a pointer to function is invoked in the same way as the function itself. That is,
const vector<int> *pseq = seq_ptr( pos );
is an indirect invocation of the function addressed by seq_ptr. We don't know (or care) what function it addresses. It might be wise, however, to confirm that it does at least address some function:
if ( ! seq_ptr )
display_message( "Internal Error: seq_ptr is set to null!" );
A pointer to function can be initialized or assigned either 0 to indicate that it addresses no function
const vector<int>* (*seq_ptr)( int ) = 0;
or the address of a function. The next question is, how do we access a function's address? It's one of the least complicated operations in C++. We just name it
// assigns seq_ptr the address of pell_seq()
seq_ptr = pell_seq;
What if we wish to set seq_ptr to a different sequence function with each iteration of our display loop without having to name each function explicitly? To solve this problem, we can again resort to indexing into an array. In this case, we define an array of pointers to functions:
// seq_array is an array of pointers to functions
const vector<int>* (*seq_array[])( int ) = {
fibon_seq, lucas_seq, pell_seq,
triang_seq, square_seq, pent_seq
};
seq_array is an array of pointers to functions holding six elements. The first element is the address of fibon_seq(), the second, lucas_seq(), and so on. We can set seq_ptr with each iteration of a while loop while the user still wishes to guess another sequence:
int seq_index = 0;
while ( next_seq == true )
{
seq_ptr = seq_array[ ++seq_index ];
// ...
}
Alternatively, what if we wished to access the pointer explicitly to the function generating the Pell sequence? It is somewhat clumsy having to remember that the Pell instance is addressed by the third array element. A more intuitive method of indexing is to provide a set of mnemonic constant values. For example,
enum ns_type {
ns_fibon, ns_lucas, ns_pell,
ns_triang, ns_square, ns_pent
};
An enumerated type is defined by the keyword enum followed by an optional identifier, such as ns_type. eThe items in the comma-separated list of named values within curly braces are called enumerators. By default, the first enumerator is assigned a value of 0. Each subsequent enumerator is given a value that is 1 greater than its predecessor. For ns_type, ns_fib has an associated value of 0, ns_lucas, 1, ns_pell, 2, and so on through ns_pent, which has an associated value of 5.
To access a particular pointer to function explicitly, we use the associated enumerator as the index:
seq_ptr = seq_array[ ns_pell ];
|