Function Pointers

Status
Not open for further replies.

Baljeet

Member
Jan 31, 2011
76
0
C++ Function Pointers

Now, as C++ programmers, we love writing messy, unreadable and advanced-looking code that will leave beginners head-scratching for hours. Function pointers happens to be a great tool for achieving maximum confusion and frustration. Did I mention that they are actually useful too?

So, what on earth is a function pointer? Function pointers are simply pointers that point to functions, or to be more specific, function addresses. Let us take a few seconds and just think about that. Done? Then we will move on. Function pointers can be very useful, and very elegant, as we will see, but their syntax is quite awkward and messy. Let us take a look at one simple function pointer right here:

Code:
int (*pMyFuncPointer) (char, int);

pMyFuncPointer is our function pointer. Kind of like normal pointers - who point to a specific datatype - function pointers point to specific function signatures. In this case, we have a pointer which is able to point to any function taking a char and an int as arguments, and returns an int.

Let's see our pointer in action, shall we?

Code:
#include <iostream>

int Foo(char, int); // First we need a function with matching signature
int (*pMyFuncPointer) (char, int) = &Foo; // We declare our function pointer, and initializes it to point at Foo. 
                                          // The reference operator can usually be omitted, but to write portable code, 
                                          // leave it there. It's also a bit clearer, IMO

int main()
{
	(*pMyFuncPointer)('d', 9); // Calls Foo.
	pMyFuncPointer('d', 9); // Also calls Foo
		
	system("PAUSE");	
	return 0;
}

int Foo(char c, int i)
{
	std::cout << "I'm Foo. It's a funny name" << 
       "\nVariable c: " << c << "\nVariable i: " << i << std::endl;
	return 0;
}

This program uses the function pointer pMyFuncPointer to call the function Foo, which prints some text together with its arguments. Are you still with me?

Ok, so function pointers can be used to call functions. There is a lot more to it, but before we go any further, I would like to show you a few other types of function pointers. The previous example would work in C, except from the std::cout part, but the example below includes classes, and is limited to C++ programmers:

Code:
#include <iostream>

class FooClass
{
public:
       void Foo1(int i) {std::cout << "Foo1's integer: " << i << std::endl;} 
       void FooConst(int i) const {std::cout << "FooConst's integer: " << i << std::endl;}
};

int main()
{
    /* ASSIGNING TO CLASS MEMBER FUNCTIONS */
    void (FooClass::*pFoo)(int) = NULL;
    void (FooClass::*pFooConst)(int) const = NULL;        
    
    pFoo = &FooClass::Foo1;             
    pFooConst = &FooClass::FooConst;    
        
    /* CALLING CLASS MEMBER FUNCTIONS */
    FooClass instance1;       
    (instance1.*pFoo)(67);        
    (instance1.*pFooConst)(57);
       
    FooClass* instance2 = new FooClass;  
    (instance2->*pFoo)(47);       
    (instance2->*pFooConst)(37);               
    delete instance2;	
    
    system("PAUSE");	
    return 0;
}

The example basically shows how to assign a pointer to a class member function, here in two steps (initializing to NULL, then assigning), and how to call these functions.

It is time to move on a bit. To begin with, function pointers can be compared, like other variables. Have a look at this:

Code:
#include <iostream>

void (*pFoo)(void) = NULL;

void Foo(void)
{
    pFoo = &Foo;    
}

int main()
{
    Foo();
    
    if(pFoo == &Foo) // Comparison of a function pointer and a function address
        std::cout << "You bastard, you changed my variable!" << std::endl;    
        
    system("PAUSE");	
    return 0;
}

You can also do comparisons like

Code:
if(pFoo != NULL)
	// ...

Now we will look at how one can pass function pointers as arguments to a function. Here is a classic example:

Code:
#include <iostream>

float Divide(float num1, float num2) {return num1 / num2;}
float Multiply(float num1, float num2) {return num1 * num2;}
float Minus(float num1, float num2) {return num1 - num2;}
float Plus(float num1, float num2) {return num1 + num2;}

float Calc(float num1, float num2, float (*pCalcMethod)(float, float))
{
    return pCalcMethod(num1, num2);
}

int main()
{
    float result = Calc(50, 25, &Plus);
    //float result = Calc(50, 25, &Minus);
    //float result = Calc(50, 2, &Multiply);
    //float result = Calc(50, 2, &Divide);

    std::cout << "Result: " << result << std::endl;
    system("PAUSE");	
    return 0;
}

When we call Calc, we specify (using function pointers) what kind of operation we want to perform on the numbers. Notice how one declares a function pointer as an argument (not very surprising, really).

Code:
float Calc(float num1, float num2, float (*pCalcMethod)(float, float))
 // ...

The function Calc accepts two floats, and a pointer to a function which accepts two floats, and returns a float.

We keep moving, this time to returning function pointers from functions. And this is a true mess. Check it out:

Code:
#include <iostream>

float Multiply(float num1, float num2) {return num1 * num2;} 
float (*GetFPointer(void))(float, float) {return &Multiply;}

int main()
{
    float (*pMultFunc)(float, float) = GetFPointer();
    
    float result = pMultFunc(2, 5);
    
    std::cout << "Result: " << result << std::endl;
	
    system("PAUSE");	
    return 0;
}

This is the tough one, right?

Code:
float (*GetFPointer(void))(float, float)
 // ...

It means that GetFPointer is a function which takes a void (i.e. nothing), and returns a pointer to a function which takes two floats and returns a float. Because of the messy syntax, this is usually simplified through an innocent typedef.

Code:
#include <iostream>

typedef float(*pFunc)(float, float);

float Multiply(float num1, float num2) {return num1 * num2;} 
pFunc GetFPointer(void) {return &Multiply;}

int main()
{
    float (*pMultFunc)(float, float) = GetFPointer();
    
    float result = pMultFunc(2, 5);
    
    std::cout << "Result: " << result << std::endl;
	
    system("PAUSE");	
    return 0;
}

Because of this typedef:

Code:
typedef float(*pFunc)(float, float);

There is now a type (pFunc) which declares a pointer to a function that takes two floats and returns a float. Exactly what we need. We can even use it to declare our function pointer in main:

Instead of:

Code:
float (*pMultFunc)(float, float) = GetFPointer();

We can write:

Code:
pFunc pMultFunc = GetFPointer();

Not as messy, fun and confusing, but it does the job.

Now, to another subject: Arrays of function pointers. These were really useful in C. While our fancy C++ classes were not around, old C-structs could still contain arrays of function pointers. Don't laugh. These arrays are very useful, or at least cool.

Code:
#include <iostream>

float Plus(float num1, float num2) {return num1 + num2;}
float Minus(float num1, float num2) {return num1 - num2;}
float Multiply(float num1, float num2) {return num1 * num2;}
float Divide(float num1, float num2) {return num1 / num2;}

float Calc(float num1, float num2, float (*pCalcMethod)(float, float))
{
    return pCalcMethod(num1, num2);
}

int main()
{
    float (*pFoo[])(float, float) = {&Plus, &Minus, &Multiply, &Divide};    
    
    float result;
    
    for(int i = 0; i < 4; ++i)
    {
        result = Calc(10, 2, pFoo[i]);
        std::cout << "Result: " << result << std::endl;
    }
	
    system("PAUSE");	
    return 0;
}

The above program illustrates the concept. Actually, it just modifies our calc-program from earlier. Now, we run through the different calculations using an array of function pointers:

Code:
float (*pFoo[])(float, float) = {...};

The braces after *pFoo tells us: "Hey! It's an array!" The size is determined from the number of initializing elements in the following curly braces "{...}", just like a normal array. We can also write e.g.

Code:
float (*pFoo[10])(float, float);

Thus declaring an array of 10 function pointers.

Array declarations like this are - by the way - often made simpler by the use of a typedef:

Code:
#include <iostream>

[B]typedef float(*pFooType)(float, float);[/B]

float Plus(float num1, float num2) {return num1 + num2;}
float Minus(float num1, float num2) {return num1 - num2;}
float Multiply(float num1, float num2) {return num1 * num2;}
float Divide(float num1, float num2) {return num1 / num2;}

float Calc(float num1, float num2, float (*pCalcMethod)(float, float))
{
    return pCalcMethod(num1, num2);
}

int main()
{
    [B]pFooType pFoo[] = {&Plus, &Minus, &Multiply, &Divide}; // Made simpler using typedef[/B]
    
    float result;
    
    for(int i = 0; i < 4; ++i)
    {
        result = Calc(10, 2, pFoo[i]);
        std::cout << "Result: " << result << std::endl;
    }
	
    system("PAUSE");	
    return 0;
}

Now, guys, this is the last thing I want to show you. It is pretty close to a C++ syntax nightmare (I did that on purpose), but it should sum up what we have been through so far. The source file for this program (the one below) can be downloaded below.

Code:
/* 
    
    Program:        FunctionPointersTestStuff
    Program type:   Useless
    Copyright:      marwex89 - www.codecall.net
    
    This program defines a class which uses a vector to store function pointers. 
    The program adds function pointers to this vector through the use of function pointers,
    and later retrieves them (using function pointers) and calls the functions they point to.
    
*/

#include <iostream>
#include <sstream>
#include <vector>

// Useless function for adding two characters into a string, and add the number 2
const std::string AddToStr_1(char a, char b) 
{
    std::ostringstream oss;
    oss << a << b << 1;
    return oss.str();     
}

// Useless function for adding two characters into a string, and add the number 2
const std::string AddToStr_2(char a, char b) 
{
    std::ostringstream oss;
    oss << a << b << 2;
    return oss.str();
}

// Useless function for adding two characters into a string, and add the number 3
const std::string AddToStr_3(char a, char b) 
{
    std::ostringstream oss;
    oss << a << b << 3;
    return oss.str();
}

// Useless function for adding two characters into a string, and add the number 4
const std::string AddToStr_4(char a, char b) 
{
    std::ostringstream oss;
    oss << a << b << 4;
    return oss.str();
}

// Useless function for adding two characters into a string, and add the number 5
const std::string AddToStr_5(char a, char b) 
{
    std::ostringstream oss;
    oss << a << b << 5;
    return oss.str();
}

// Useless class for storing function pointers
class CFuncPointerStorage
{   
private:
    
    std::vector<const std::string(*)(char, char)> vec;
        
public:
        
    const std::string(*GetFunctionPointer(unsigned int index))(char, char) const
    {
        if(index <= vec.size())
            return vec[index];
        else
        {
            std::cerr << "Weird error occured, you need a new computer." << std::endl;
            return NULL;
        }
    } 
    
    void SetFunctionPointer(const std::string(*pFunctionPointer)(char, char), unsigned int index)
    {
        if(index < vec.size())
            vec[index] = pFunctionPointer;
        else
            vec.push_back(pFunctionPointer);
    }           
};

// Using the stuff
int main()
{
    CFuncPointerStorage* fpStorage = new CFuncPointerStorage;
    
    // Put function pointers into storage object
    const std::string(*fpArray[])(char, char) = {&AddToStr_1, &AddToStr_2, &AddToStr_3, &AddToStr_4, &AddToStr_5};    
    for(int i = 0; i < 5; ++i)
        fpStorage->SetFunctionPointer(fpArray[i], i);
    
    // Get them back and perform calls
    for(int i = 0; i < 5; ++i) 
        std::cout << (*(fpStorage->GetFunctionPointer(i)))('I', 'T') << std::endl;
    
    delete fpStorage;
    	
    system("PAUSE");	
    return 0;
}

Well, my friends, that was it. I have attached the code for the last program (the one above). Hopefully you found this tutorial helpful - feel free to comment and +rep - I appreciate it. See you later ;)

You must be registered for see images attach


All Credits goes to one who really made this...
 
Status
Not open for further replies.

Users who are viewing this thread

Top