Ok, here's the background. Let's say you want to be able to find the cosine of an array of doubles, but you also want to be able to find the sine, tangent, etc.

You could do this:

#include <math.h>voidapply_sin(double*dat,intlength,inttype){intk;for(k=0;k<length;k++){dat[k] = sin(dat[k]);}}voidapply_cos(double*dat,intlength,inttype){intk;for(k=0;k<length;k++){dat[k] = cos(dat[k]);}}voidapply_tan(double*dat,intlength,inttype){intk;for(k=0;k<length;k++){dat[k] = tan(dat[k]);}}voidapply_square(double*dat,intlength,inttype){intk;for(k=0;k<length;k++){dat[k] *= dat[k];}}

But this leaves a lot to be desired. Each operation requires a new function that is nearly identical to all the rest. Can't we factor out the commonality?

Ok, how about this:

#include <math.h>#include <stdio.h>doublesquare(doubleval){returnval * val;}voidapply_operation(double*dat,intlength,double(*oper)(double)){intk;for(k=0;k<length;k++){dat[k] = oper(dat[k]);}}voidprint_vector(double*dat,intlength){intk;for(k=0;k<length;k++){printf("%g ", dat[k]);}printf("\n");}voidcall_it(void){doublevals[] ={1,2,3}; apply_operation(vals,sizeof(vals)/sizeof(vals[0]), sin); print_vector(vals,sizeof(vals)/sizeof(vals[0])); apply_operation(vals,sizeof(vals)/sizeof(vals[0]), cos); print_vector(vals,sizeof(vals)/sizeof(vals[0])); apply_operation(vals,sizeof(vals)/sizeof(vals[0]), tan); print_vector(vals,sizeof(vals)/sizeof(vals[0])); apply_operation(vals,sizeof(vals)/sizeof(vals[0]), square); print_vector(vals,sizeof(vals)/sizeof(vals[0]));}intmain(intargc,char*argv[]){call_it();}

So that's a bit better. Only one implementation of the loop code, simple implementations for each operation, only the overhead of an additional pointer dereference per element. How did we pull this off?

voidapply_operation(double*dat,intlength,double(*oper)(double))

apply_operation() takes a function pointer as a parameter (the last argument). This makes it so that apply_operation() can take any function that takes a single double as input and returns a single double as output. Slick.

Now, the next step: returning a function pointer (without typedefing the signature). Let's say we have a table of operations and we associate each operation type with a function pointer so that we can do a simple lookup to get a pointer to the desired operation function. So, in other words, we're returning a function pointer. Here's how to do it

*without*a typedefed function signature.

#include <math.h>#include <stdio.h>enum{OPER_SIN, OPER_COS, OPER_TAN, OPER_SQUARE};structoper{inttype;double(*func)(double);};doublesquare(doubleval){returnval * val;}structoper oper_lut[] ={{OPER_SIN, sin},{OPER_COS, cos},{OPER_TAN, tan},{OPER_SQUARE, square},};double(*get_oper(intoper_type))(double){intn;for(n=0;n<(sizeof(oper_lut)/sizeof(oper_lut[0]));n++){if(oper_type == oper_lut[n].type){returnoper_lut[n].func;}}}voidapply_operation(double*dat,intlength,double(*oper)(double)){intk;for(k=0;k<length;k++){dat[k] = oper(dat[k]);}}voidprint_vector(double*dat,intlength){intk;for(k=0;k<length;k++){printf("%g ", dat[k]);}printf("\n");}voidcall_it(void){doublevals[] ={1,2,3};double(*func)(double); func = get_oper(OPER_SIN); apply_operation(vals,sizeof(vals)/sizeof(vals[0]), func); print_vector(vals,sizeof(vals)/sizeof(vals[0])); func = get_oper(OPER_COS); apply_operation(vals,sizeof(vals)/sizeof(vals[0]), func); print_vector(vals,sizeof(vals)/sizeof(vals[0])); func = get_oper(OPER_TAN); apply_operation(vals,sizeof(vals)/sizeof(vals[0]), func); print_vector(vals,sizeof(vals)/sizeof(vals[0])); func = get_oper(OPER_SQUARE); apply_operation(vals,sizeof(vals)/sizeof(vals[0]), func); print_vector(vals,sizeof(vals)/sizeof(vals[0]));}intmain(intargc,char*argv[]){call_it();}

The magic is in get_oper(). Did you catch it?

double(*get_oper(intoper_type))(double)

This means that get_oper() is a function that takes a single int as a parameter and returns a function pointer to a function that returns a double and takes a single double as its only argument. Simple, right? There it is--returning a function pointer from a function without typedefing the signature (the "hard" way).

This is the best reference I've seen so far on function pointers http://www.newty.de/fpt/fpt.html.

## No comments:

Post a Comment