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
[edit | edit source]- C knowledge
Objectives
[edit | edit source]- 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
[edit | edit source]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
[edit | edit source]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
[edit | edit source]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 <stdio.h> 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 <stdio.h> 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 <stdio.h> 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; }