So I was wondering how they work. To explain what I mean by a "function caller" a good example of what I mean would be glutTimerFunc
, it is able to take in a function as a parameter and call it even with it not knowing it is declared. How is it doing this?
views:
118answers:
3What you're talking about is called a callback and is implemented with function pointers in C and C++.
Since you mentioned glut, let's take a real example directly from the freeglut source code. I'll use glutIdleFunc instead of glutTimerFunc because the code is simpler.
In glut, the idle function callback (what you supply to glutIdleFunc) is a pointer to a function that takes no parameters and returns nothing. A typedef is used to give such a function type a name:
typedef void (* FGCBIdle)( void );
Here, FGCBIdle (short for FreeGlut CallBack Idle) is defined as a pointer to a function that takes no parameters and whose return type is void. This is just a type definition that makes writing expression easier, it doesn't allocate any storage.
Freeglut has a structure called SFG_State that holds various settings. Part of the definition of that structure is:
struct tagSFG_State
{
/* stuff */
FGCBIdle IdleCallback; /* The global idle callback */
/* stuff */
};
The struct holds a variable of type FGCBIdle, which we established is another name for a specific function pointer. You can set the IdleCallback field to point to the address of a function that is supplied by the user using the glutIdleFunc function. The (simplified) definition of that function is:
void glutIdleFunc( void (* callback)( void ) )
{
fgState.IdleCallback = callback;
}
Here, fgState is a SFG_State variable. As you can see, the glutIdleFunc takes one parameter which is a function pointer to a function that takes no parameters and returns nothing, this parameter's name is callback. Inside the function, the IdleCallback inside the global fgState variable is set to the user supplied callback. When you call the glutIdleFunc function, you pass the name of your own function (e.g. glutIdleFunc(myIdle)), but what you're really passing is the address of the function.
Later, inside the big glut event processing loop initiated by glutMainLoop, you'll find this code:
if( fgState.IdleCallback )
{
/* stuff */
fgState.IdleCallback( );
}
If a user supplied idle callback is available, it is called in the loop. If you check the function pointer tutorial at the beginning of my post you will understand the syntax better, but I hope the general concept makes more sense now.
The parameter is a pointer to a function. It is the caller's responsibility to ensure that the function declaration matches the requirements (e.g., number and type of parameters, calling convention).
A function passed as a paramter is passed as a function pointer.
In compiled code, a function is nothing more than an address which the CPU can vector off to to execute. When you are passing a function pointer, the compiler (and later the linker) insert the correct address into the call.
Function parameters must match exactly as the executing code will simply push values on the stack (or registers, depending on the architecture) and expect to pop off (read out) the return value.