Game programming/Function pointers

In C, pointers are everywhere. You are probably familiar with pointers to variables. Maybe even pointers to structs. A pointer is a variable which holds a memory address. It usually has some useful value, like a acting as a reference to a variable in a different scope. In C, you can also have pointers to functions.

Requirements

 * C knowledge

Objectives

 * To know how to make a pointer to a function
 * To know how to use a pointer to a function
 * To get to know some usage examples of pointers to functions

Introduction
First, let's look at a function declaration. double getAvg(double a, double b)   { return (a + b) / 2; } As you can see, we have here a function called "getAvg". It receives two doubles and returns a double.

Explanation
A pointer to a function does looks mostly not different from a regular function, except it has the '*' symbol as in all pointers, and parenthesis as follows: /* Declaration */ double (*fp(double, double)); // Or double (*fp)(double, double); or just double (*fp); /* Assignment */ fp = &getAvg; /* Use */ fp(2, 3); // We can also use (*fp)(2, 3); to be more explicit about the fact that fp is a pointer - but it is a case of personal preference. Here we have a pointer called "fp", so we'll refer to it as such. It is a pointer to a function that receives two doubles and returns a double. Note how we do not add the parenthesis after getAvg when we assign it's address to fp. Also note the '&' operator. Using a pointer to a function is not different from using a normal function. Hence, we could do the following: printf("fp(2, 3) = %f.2\n", fp(2, 3));

Application
Pointers to functions serve three general purposes. One is that they provide a way to have methods in structs, the second is that they allow for some degree of polymorphism, and the third is passing functions to functions.

To create methods in a struct, #include    typedef struct {       double a;        double b;        double (*getAvg)(MyStruct_t*); } MyStruct_t; double _MyStruct_t_getAvg(MyStruct_t* this) {       return (this->a + this->b) / 2; }   MyStruct_t* createMyStruct(double da, double db) {       MyStruct_t* new = (MyStruct_t*) malloc(sizeof(MyStruct_t)); new->a = da; new->b = db; new->getAvg = &_MyStruct_t_getAvg; return new; }   int main(int argc, char* argv[]) {       MyStruct_t* strct = createMyStruct(2, 3); printf("getAvg = %f.2", strct->getAvg(strct)); return 0; } Note: This example isn't that useful. It requires work. Note: Unless each struct has a possibly different pointer (ie. you aren't just trying to make OOP), then you are wasting space! Just because you can make OOP work in C doesn't mean you should - or even that it's good to do so.

As an example of polymorphism, #include    void doOne(void) {       printf("I've done One\n"); }   void doTwo(void) {       printf("I've done Two\n"); }   void doNothing(void) {       printf("I've done nothing\n"); }   int main(int argc, char* argv[]) {       void (*work)(void) = doNothing; int state; for(state = 0; state < 2; ++state) {           if(state == 0) {               work = &doOne; }           else if(state == 1) {               work = &doTwo; }           else {               work = &doNothing; }           printf("State %d,\n", state); work; }       printf("Last work:\n"); work; return 0; } Note: you can create an array of function pointers void (*function_pointers[]) to allow cycling through functions if strictly necessary. Just remember, you the correct expected return operand when cycling through - so if you're switching some pointer through these values, they'd better all have at least the same return size, and preferably same return type.

And an example of sending functions through functions, #include    void doWork(void (*beforeBegin)(void), void (*afterDone)(void)) {       beforeBegin; printf("I've done some work.\n"); afterDone; }   void logBegin(void) {       printf("LOG: I'm about to do some work.\n"); }   void logEnd(void) {       printf("LOG: I'm done doing some work.\n"); }   int main(int argc, char* argv[]) {       doWork(logBegin, logEnd); return 0; }