The Allegro Wiki is migrating to github at https://github.com/liballeg/allegro_wiki/wiki

Function pointers

From Allegro Wiki
Revision as of 18:28, June 16, 2009 by Matt Smith (talk | contribs) (Syntax)
Jump to: navigation, search

Overview

In C and C++ function pointers are a way of abstracting and referring to pieces of executable code. This way you can index pieces of code, by making an array of function pointers, or pass routines between functions as argument or return-value. Normally each function has an address, which is most notably used by the CPU to determine what code to execute. In C you can take the address of any non-member function. You can refer to this address simply by referring to the name of the function or using the address operator. Any normal pointer operation is possible on a function pointer, except for pointer increment and decrement. C++ has some features that are more troublesome to combine with function pointers. In explicitas you cannot use the address of a member function, and overloaded functions require a cast when used in templates. There are performance implications, because the compiler can't optimize a call to some arbitrary function like it can to a known one. C++ has two features that greatly reduce the (client programmer) use of function pointers. Namely polymorphism and function objects (a.k.a. functors).

Syntax

Examples the use of function pointers. <highlightSyntax language="cpp"> /*

*  Declares a function pointer named funcptr that accepts two ints and returns
*  an int. Even more dangerous than a normal pointer to leave uninitialized!
*  Notice the * operator is on the left of the identifier and grouped inside
*  parenthesis. The return value is int, not (int *). If you wanted to return
*  (int *) you would have an extra * on the left of the parenthesis like so:
*
*  int *(*funcptr)(int, int);
*
*/
int (*funcptr)(int, int);

/*

* Basic usage. Syntactily you can treat function pointers and functions a lot alike.
*/
int my_function(int, int); //Function declared elsewhere called my_function.
funcptr = &my_function;    //This line assigns funcptr to the address of my_function.
funcptr = my_function;     //This line also assigns funcptr to the address of my_function.
(*my_fct_ptr)(1, 2);       //This line executes the function pointed to by funcptr.
my_fct_ptr(3 , 4);         //This line executes the function pointed to by funcptr.
typeof(my_function) == typeof(funcptr);//This is true: my_function without parentheses can
//only be cast to, and intrepreted as a function pointer.

/*

* Declares a function pointer type named MyFuncPtrType that accepts two ints and
* returns an int.
*/

typedef int (*MyFuncPtrType)(int, int);

/*

* Here's a function that matches the MyFuncPtrType type. It accepts two ints,
* and returns an int.
*/

int my_sumer(int x, int y) {

   return(x + y);

}

// Here's a function that accepts a function pointer as an argument and calls it. int my_caller(MyFuncPtrType f) {

   return(f(5, 5));

}

// Same function without use of typedef int my_caller_ex(int(*f)(int, int)) {

   return(f(5, 5));

}

int main(int argc, char *argv[]) {

   /*
    * Here we are passing a function pointer as an argument, which is
    * subsequently called, and whose result is then returned.
    */
   int result = my_caller(my_sumer);
   printf("The result is %d.\n", result);
   return(0);

} /*

* Ilustrating how to point to the (certainly) overloaded function foo in C++
* with a template function caller_f
*/

void foo() {

   std::cout << "foo()\n";

}

void foo(int) {

   std::cout << "foo(int)\n";

}

template <class Fun> void caller_f(Fun f) {

   f();

}

//caller_f(foo); //Error: can't deduce which foo to use caller_f<void(*)()>(foo); //OK, explicit instantiation

caller_f(static_cast<void(*)()>(foo)); //OK, static_cast disambiguates the type

void (*fun)() = &foo; //OK, the type of fun tells which overload to pick caller_f(fun); //OK, the type of fun is known


/*

* Using a template function to initiate the pointer in C++ is fine however.
*/

template<typename T> void bar_f(T in){

 std::cout << "barf:" << in << std::endl;

}

void (*f_ptr_takes_int)(int) = bar_f<int>;

</highlightSyntax>

Exotic examples

<highlightSyntax language="cpp"> /*

* funcC Is a function pointer to a function takes
* a function pointer to
*   a function that takes
*     a function pointer .
*/
void (*funcC)(void(*)(void(*)(void)));

/*

* cp1 Is a function pointer to a function that takes an int and
* returns a pointer to an array of ten char pointers.
*/
char* (*(*cp)(int))[10];

/*

* ip Is a function that returns a pointer to a function that
* takes a char argument and returns an int.
*/
int (*(*ip()))(char);

</highlightSyntax>

Examples of use

Libc's qsort takes a function pointer which determines what nodes are higher-ranked. (C++'s STL uses functors in the same fashion.) Allegro's blit() uses a function pointer that is initialised by install_allegro() and points to some platform-specific implementation of blit(). (It is done so by a socalled vtable.) Allegro's do_line() does something similar.


Contributors: anonymous, bamccaig, Dustin Dettmer, Evert, KibiZ0r, KittyCat, Tobias Dammers, torhu [References] Thinking in C++ 2nd edition Volume 1 (chapter 3), Bruce Eckel 2000 [1] todo: links to source at examples