Function Pointers and Callbacks in C++

Function Pointers and Callbacks in C++
Introduction to Function Pointers: Function Pointers provide some extremely interesting, efficient and elegant programming techniques. You can use them to replace switch/if-statements, to implement callbacks.
If you call a virtual function, your program has got to determine which one has got to be called. It does this using a V-Table containing all the possible functions. This costs some time each call and maybe you can save some time using function pointers instead of virtual functions.
Function Pointer: Function Pointers are pointers, i.e. variables, which point to the address of a function. You must keep in mind, that a running program gets a certain space in the main-memory. Both, the executable compiled program code and the used variables, are put inside this memory. Thus a function in the program code is, like e.g. a character field, nothing else than an address. It is only important how you, or better your compiler/processor, interpret the memory a pointer points to.
Example: Using Switch-Statement
When you want to call a function DoIt() at a certain point called label in your program, you just put the call of the function DoIt() at the point label in your source code. Then you compile your code and every time your program comes up to the point label, your function is called.
A switch-statement, where you call the functions just like you want it, in the different branches, another wayis to Use a function pointer.
The task is first solved using a switch-statement. Then it is shown, how the same can be done using a function pointer.
Example: Using Switch statement

// Task: Perform one of the four basic arithmetic operations specified
//the characters ’+’, ’-’, ’*’ or ’/’.
// The four arithmetic operations … one of these functions is selected
// at runtime with a swicth or a function pointer
float Plus (float a, float b) { return a+b; }
float Minus (float a, float b) { return a-b; }
float Multiply(float a, float b) { return a*b; }
float Divide (float a, float b) { return a/b; }
// Solution with a switch-statement – specifies which operation to execute
void Switch(float a, float b, char opCode)
{
float result;
// execute operation
switch(opCode){
case ’+’ : result = Plus (a, b); break;
case ’-’ : result = Minus (a, b); break;
case ’*’ : result = Multiply (a, b); break;
case ’/’ : result = Divide (a, b); break; }
cout << “Switch: 2+5=” << result << endl; // display result
}
// Solution with a function pointer – is a function pointer and points to
// a function which takes two floats and returns a float. The function pointer
// “specifies” which operation shall be executed.
void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(float, float))
{
float result = pt2Func(a, b); // call using function pointer
cout << “Switch replaced by function pointer: 2-5=”; // display result
cout << result << endl;
}
// Execute example code
void Replace_A_Switch()
{
cout << endl << “Executing function ’Replace_A_Switch’” << endl;
Switch(2, 5, /* ’+’ specifies function ’Plus’ to be executed */ ’+’);
Switch_With_Function_Pointer(2, 5, /* pointer to function ’Minus’ */ &Minus);
}
A function pointer always points to a function with a specific parameters and return-type. Thus all functions, you want to use with the same function pointer, must have the same parameters and return-type.
Syntax Of Function Pointers: Regarding their syntax, there are two different types of function pointers: On the one hand there are pointers to ordinary C functions or to static C++ member functions. On the other hand there are pointers to non-static C++ member functions. The basic difference is that all pointers to non-static member functions need a hidden argument: The this-pointer to an instance of the class. Always keep in mind: These two types of function pointers are incompatible with each other.
Definition: A function pointer is nothing else than a variable, it must be defined as usual.
Example defines two function pointers named pt2Function, pt2Member and pt2ConstMember which to point to functions, which take one float and two char and return an int.
In the C++ example it is assumed, that the functions, our pointers point to, are (non-static) member functions of TMyClass.
// Define a function pointer and initialize to NULL
int (*pt2Function)(float, char, char) = NULL; // C
int (TMyClass::*pt2Member)(float, char, char) = NULL; // C++
int (TMyClass::*pt2ConstMember)(float, char, char) const = NULL; // C++
Calling Convention: Normally you don’t have to think about a function’s calling convention: The compiler assumes cdecl as default if you don’t specify another convention.
The calling convention tells the compiler how to pass the arguments or how to generate the name of a function. The calling convention belongs to a functions signature: Thus functions and function pointers with different calling convention are incompatible with each other.
// define the calling convention
void __cdecl DoIt(float a, char b, char c); // Borland and Microsoft
void DoIt(float a, char b, char c) __attribute__((cdecl)); // GNU GCC
Assign an Address to a Function Pointer: By the name of a suitable and known function or member function, including class-name and scope-operator (::).
Also you have got to ensure, that you are allowed to access the function right in scope where your assignment stands.
// assign an address to the function pointer
// Note: Although you may ommit the address operator on most compilers
// you should always use the correct way in order to write portable code.
// C
int DoIt (float a, char b, char c){ printf(“DoIt\n”); return a+b+c; }
int DoMore(float a, char b, char c)const{ printf(“DoMore\n”); return a-b+c; }
pt2Function = DoIt; // short form
pt2Function = &DoMore; // correct assignment using address operator
// C++
class TMyClass
{
public:
int DoIt(float a, char b, char c){ cout << “TMyClass::DoIt”<< endl; return a+b+c;};
int DoMore(float a, char b, char c) const
{ cout << “TMyClass::DoMore” << endl; return a-b+c; };
/* more of TMyClass */
};
pt2ConstMember = &TMyClass::DoMore; // correct assignment using address operator
pt2Member = &TMyClass::DoIt; // note: may also legally point to &DoMore
Comparing Function Pointers : You can use the comparison-operators (==, !=) the same way as usual. In the following example it is checked, whether pt2Function and pt2Member actually contain the address of the functions DoIt and TMyClass::DoMore.
Example using equality.
// comparing function pointers
// C
if(pt2Function >0){ // check if initialized
if(pt2Function == &DoIt)
printf(“Pointer points to DoIt\n”); }
else
printf(“Pointer not initialized!!\n”);
// C++
if(pt2ConstMember == &TMyClass::DoMore)
cout << “Pointer points to TMyClass::DoMore” << endl
Calling a Function using a Function Pointer: In C you call a function using a function pointer by explicitly dereferencing it using the * operator. Alternatively you may also just use the function pointer’s instead of the funtion’s name. In C++ the two operators .* resp. ->* are used together with an instance of a class in order to call one of their (non-static) member functions. If the call takes place within another member function you may use the this-pointer.
// Example Calling a function using a function pointer
int result1 = pt2Function (12, ’a’, ’b’); // C short way
int result2 = (*pt2Function) (12, ’a’, ’b’); // C
TMyClass instance1;
int result3 = (instance1.*pt2Member)(12, ’a’, ’b’); // C++
int result4 = (*this.*pt2Member)(12, ’a’, ’b’); // C++ if this-pointer can be used
TMyClass* instance2 = new TMyClass;
int result4 = (instance2->*pt2Member)(12, ’a’, ’b’); // C++, instance2 is a pointer
delete instance2;
How to Pass a Function Pointer as an Argument: You can pass a function pointer as a function’s calling argument. In the example shows how to pass a pointer to a callback function which returns an int and takes a float and two char:
//————————————————————————————
// How to Pass a Function Pointer
// is a pointer to a function which returns an int and takes a float and two char
void PassPtr(int (*pt2Func)(float, char, char))
{
int result = (*pt2Func)(12, ’a’, ’b’); // call using function pointer
cout << result << endl;
}
// execute example code – ’DoIt’ is a suitable function like defined above in 2.1-4
void Pass_A_Function_Pointer()
{
cout << endl << “Executing ’Pass_A_Function_Pointer’” << endl;
PassPtr(&DoIt);
}
Return a Function Pointer: A function pointer can be a function’s return value.
Example of how to return a pointer to a function which is taking two float arguments and returns a float. If you want to return a pointer to a member function you have just got to change the definitions/declarations
of all function pointers.
//————————————————————————————
// How to Return a Function Pointer
// ’Plus’ and ’Minus’ are defined above. They return a float and take two float
// Direct solution: Function takes a char and returns a pointer to a
// function which is taking two floats and returns a float.
// specifies which function to return
float (*GetPtr1(const char opCode))(float, float){
if(opCode == ’+’)
return &Plus;
else
return &Minus;} // default if invalid operator was passed

No comments: