Les pointeurs de fonctions
Le langage C possède une fonctionnalité fort utile pour ajouter de la souplesse à vos programmes: les pointeurs de fonctions. On verra qu'une fonction peut être manipulé comme une variable via un pointeur vers l'adresse de la fonction, on pourra donc la stocker dans des structures, et la passer en paramètres à d'autres fonctions.
Un pointeur de fonction, est un pointeur qui pointe vers l'adresse d'une fonction.
Un exemple:
#include <stdio.h>
#include <stdlib.h>
void print_it(char *string){
printf(string);
}
void gtk_print_it(char *string){
/* same thing in gtk UI */
}
void do_something(void (*use_to_pring)()){
/* Do something */
use_to_pring("Hola\n");
}
int main(int argc, char **argv, char **envp){
do_something(&print_it);
return EXIT_SUCCESS;
}
Évidement, l'exemple est bidon, il sert à rien, mais ça montre qu'on peut passer une fonction en paramètre, et l'appeler ainsi à travers son pointeur.
Si vous voulez vous convaincre de l'utilité de telles constructions, imaginez que vous laissiez le choix à l'utilisateur d'afficher les messages d'erreur à la console, ou dans une interface graphique. Il suffit de changer le pointeur de la fonction qui affiche pour changer le comportement à la volé, pas besoin de jouer avec des switch-case.
Principe, syntaxe
Déclarer un pointeur de fonction
Pour déclarer un pointeur de fonction, il faut donner son type de retour, ses paramètres, et ne pas oublier l'étoile devant le nom de la fonction pour spécifier le pointeur:
int (*handle_request)(int, int) = NULL;
Assigner le pointeur à la fonction
Pour donner l'adresse d'une fonction à un pointeur, il suffit de le valuer avec le nom de la fonction.
handle_request = lazy_handler;
On trouve la notation avec l'opérateur d'adresse:
handle_request = &lazy_handler;
Cette notation est plus portable mais souvent oubliée parce que tout le monde travaille avec GCC ou Intel CC.
Appeler la fonction
Pour appeler un pointeur de fonction, il suffit d'utiliser exactement la même syntaxe que pour un appel classique de fonction:
int error_code = handle_request(1,25);
À quoi ça sert?
Les pointeurs de fonctions sont particulièrement utile pour ajouter de la souplesse à vos programmes. Il faut bien réaliser qu'il est possible de changer les fonctions appelées après le lancement du programme.
Les pointeurs de fonctions sont aussi particulièrement utile pour réaliser des fonctions de callback. Avec GTK+ par exemple, c'est le moyen utilisé pour définir les fonctions devant être exécuté pour répondre aux actions de l'utilisateur (fonction de callbackk pour les évènements).
Une fois que vous avez bien acquis le concept et la syntaxe des pointeurs de fonctions, vous trouverez plein d'utilisation intéressante dans vos applications.
Aller plus loin
Si vous voulez aller plus loin que les bases, voici quelques petits détails supplémentaire.
Retourner un pointeur de fonction
On peut bien sûr retourner un pointeur de fonction comme valeur de retour d'une fonction. Néanmoins, la syntaxe est particulièrement bizarre:
int (*get_handler(int which_one))(int, int)
{
...
Ce code définit une fonction get_handler() qui reçoit un paramètre entier which_one, et retourne une fonction de signature int (*fct)(int, int).
Ce code est difficile à lire, pour plus de clarté, on préfèrera utiliser un typedef:
typedef int (*handler_type)(int, int);
handler_type get_handler(int which_one)
{
...
Un tableau de pointeur de fonction
Très pratique avec des méthodes pilotés par un tableau, vous pouvez passer un tableau de pointeur de fonction (ou une structure de pointeur de fonction c'est aussi courant).
Tout comme pour l'exemple précédent, on a une syntaxe directe qui est illisible:
int (*handler_array[5])(int,int) = {NULL};
Mais on utilise un typedef pour rendre le code plus propre:
handler_type handler_array[5] = {NULL};

