Fonction appelants (callbacks) en C?

Alors je me demandais comment ils fonctionnent. Pour expliquer ce que je veux dire par “appelant de fonction” un bon exemple de ce que je veux dire serait glutTimerFunc , il est capable de prendre une fonction en tant que paramètre et de l’appeler même si elle ne le sait pas. Comment fait-il cela?

Ce dont vous parlez s’appelle un rappel et est implémenté avec des pointeurs de fonction en C et C ++.

Puisque vous avez parlé de la surabondance, prenons un exemple réel directement à partir du code source de freeglut . Je vais utiliser glutIdleFunc au lieu de glutTimerFunc car le code est plus simple.

En cas de surabondance, le rappel de fonction inactif (ce que vous fournissez à glutIdleFunc) est un pointeur sur une fonction qui ne prend aucun paramètre et ne renvoie rien. Un typedef est utilisé pour donner un nom à ce type de fonction:

 typedef void (* FGCBIdle)( void ); 

Ici, FGCBIdle (abréviation de FreeGlut CallBack Idle) est défini comme un pointeur sur une fonction qui ne prend aucun paramètre et dont le type de retour est void. Ceci est juste une définition de type qui facilite l’écriture d’expression, elle n’alloue aucun stockage.

Freeglut a une structure appelée SFG_State qui contient divers parameters. Une partie de la définition de cette structure est la suivante:

 struct tagSFG_State { /* stuff */ FGCBIdle IdleCallback; /* The global idle callback */ /* stuff */ }; 

La structure contient une variable de type FGCBIdle, que nous avons établie est un autre nom pour un pointeur de fonction spécifique. Vous pouvez définir le champ IdleCallback pour qu’il pointe vers l’adresse d’une fonction fournie par l’utilisateur à l’aide de la fonction glutIdleFunc. La définition (simplifiée) de cette fonction est la suivante:

 void glutIdleFunc( void (* callback)( void ) ) { fgState.IdleCallback = callback; } 

Ici, fgState est une variable SFG_State. Comme vous pouvez le constater, le glutIdleFunc prend un paramètre qui est un pointeur de fonction sur une fonction qui ne prend aucun paramètre et ne renvoie rien, le nom de ce paramètre est callback. Dans la fonction, la propriété IdleCallback de la variable globale fgState est définie sur le rappel fourni par l’utilisateur. Lorsque vous appelez la fonction glutIdleFunc, vous transmettez le nom de votre propre fonction (par exemple, glutIdleFunc (myIdle)), mais vous transmettez en réalité l’adresse de la fonction.

Plus tard, vous trouverez ce code dans la boucle de traitement des événements de grande glut initiée par glutMainLoop:

 if( fgState.IdleCallback ) { /* stuff */ fgState.IdleCallback( ); } 

Si un rappel inactif fourni par l’utilisateur est disponible, il est appelé dans la boucle. Si vous consultez le didacticiel du pointeur de fonction au début de mon article, vous en comprendrez mieux la syntaxe, mais j’espère que le concept général aura plus de sens maintenant.

Le paramètre est un pointeur sur une fonction. Il incombe à l’appelant de s’assurer que la déclaration de fonction correspond aux exigences (par exemple, nombre et type de parameters, convention d’appel).

Une fonction passée en tant que paramètre est passée en tant que pointeur de fonction.

Dans le code compilé, une fonction n’est rien d’autre qu’une adresse à laquelle la CPU peut s’exercer. Lorsque vous passez un pointeur de fonction, le compilateur (et plus tard l’éditeur de liens) insère l’adresse correcte dans l’appel.

Les parameters de fonction doivent correspondre exactement car le code en cours d’exécution poussera simplement les valeurs sur la stack (ou les registres, en fonction de l’architecture) et s’attend à ce que la valeur renvoyée soit lue (lue).