Sunday, February 28, 2010

Function pointers

Pointers to functions

How one uses a pointer to a function is detailed below, but why do we do it is a good place to start. There are two common reasons for using pointers to functions and many more uncommon reasons.

1) State machine changes. A good way to manage FSM (finite state machines) is to change their functionality at runtime. Take the following two functions that take a monster class as a parameter.

class Monster;
typedef void (*UpdateFunctionPtr) (Monster*);

void UpdateFn1 (Monster*monster)
{
// ...
}
void UpdateFn2 (Monster*monster)
{
//...
}

class Monster
{
public:
UpdateFunctionPtr FunctionPointer;
};


Now the runtime application can make decisions based on state or data to change how an instance of the Monster class manages its updates. For example:


Monster monster;
if (... )
monster.UpdateFunctionPtr = & UpdateFn1;
else
monster.UpdateFunctionPtr = & UpdateFn2;

...

(*monster.UpdateFunction)(& monster);


Be very careful here. More than once, I have missed the asterisk before the function call.

All of this may seem like overkill since classes are meant to manage their own state and call the appropriate functions when things change.

However, it is common to do this for callback functions, timers, and other objects meant to call a method once their timer expires or an event fires. This brings us to reason 2.


class Timer
{
public:
UpdateFunction FunctionPointer;
void Update()
{
if(currentTime-BeginTime > totalTime)
{
(*FunctionPointer)(...);
isExpired = true;
}
}
};


This code can be made generic with the use of templates or delegates.
//-----------------------------------------------------

Pointers to member functions

The basic concept is that you should have a class with functions that you can access (public, but not required), an instance of that class, and another class which knows very little about which functions to call. This is where syntax can really hurt. Note here in the Different class, the typedef to the function pointer contains an asterisk before the generic typedef name.


class Different
{
public:
void Function1TakingTwoParameters(int x, int y);
void Function2TakingTwoParameters(int x, int y);

typedef void (Different::*FuncPtrTwoParams)(int, int);
};


Now it's often pointless to do use member function pointers unless you have a design that separates the function invocation with a delta time meant to provide a callback. Other things may be a design separating interface from implementation.


//--------------------------------------------------------------------
class SetupCallingFunction
{
public:
SetupCallingFunction( Different* diff, Different::FuncPtrTwoParams fnPtr ) : differ (diff), functor (fnPtr)
{
}

void SomethingHappens()// timer or something else
{
(differ->*functor) (1, 1);
}
Different::FuncPtrTwoParams functor;
Different* differ;
};


This probably looks a little confusing. Basically, we have a pointer to an object "differ" and therefore must use the arrow syntax. Then we have a pointer to some member function of differ which we invoke with the asterisk. This block must be enclosed by parenthesis, or it will not compile. Then the last piece is the list of parameters which is what you should expect. This is how this class above is used:



Different differ;
SetupCallingFunction caller (&differ, &Different::Function1TakingTwoParameters);

2 comments:

Unknown said...

"(monster.*UpdateFunction)(& monster);


Be very careful here. More than once, I have missed the asterisk before the function call."

Well Umm, you put the asterisk in the wrong place.

(monster.*UpdateFunction)(& monster)

should be

(*monster.UpdateFunction)(& monster)

You are not calling a member function pointer but a function pointer you would get a compiler error for that blunder.

Mickey Kawick said...

That's what I get for publishing a post that's not complete yet. Thanks.