Fonction de rappel en C++

Callback Function C



Une fonction de rappel est une fonction, qui est un argument, pas un paramètre, dans une autre fonction. L'autre fonction peut être appelée fonction principale. Deux fonctions sont donc impliquées : la fonction principale et la fonction de rappel elle-même. Dans la liste des paramètres de la fonction principale, la déclaration de la fonction de rappel sans sa définition est présente, tout comme les déclarations d'objet sans affectation sont présentes. La fonction principale est appelée avec des arguments (dans main()). L'un des arguments de l'appel de fonction principale est la définition effective de la fonction de rappel. En C++, cet argument est une référence à la définition de la fonction de rappel ; ce n'est pas la définition réelle. La fonction de rappel elle-même est en fait appelée dans la définition de la fonction principale.

La fonction de rappel de base en C++ ne garantit pas un comportement asynchrone dans un programme. Le comportement asynchrone est le véritable avantage du schéma de fonction de rappel. Dans le schéma de fonction de rappel asynchrone, le résultat de la fonction principale doit être obtenu pour le programme avant que le résultat de la fonction de rappel ne soit obtenu. Il est possible de le faire en C++ ; cependant, C++ a une bibliothèque appelée future pour garantir le comportement du schéma de fonction de rappel asynchrone.







Cet article explique le schéma de fonction de rappel de base. Une grande partie est en C++ pur. En ce qui concerne le rappel, le comportement de base de la future bibliothèque est également expliqué. Une connaissance de base du C++ et de ses pointeurs est nécessaire à la compréhension de cet article.



Contenu de l'article

Schéma de fonction de rappel de base

Un schéma de fonction de rappel a besoin d'une fonction principale et de la fonction de rappel elle-même. La déclaration de la fonction de rappel fait partie de la liste des paramètres de la fonction principale. La définition de la fonction de rappel est indiquée dans l'appel de fonction de la fonction principale. La fonction de rappel est en fait appelée dans la définition de la fonction principale. Le programme suivant illustre cela :



#comprendre

à l'aide de espace de nomsles heures;



entiermainFn(carboniserch[],entier (*ptr)(entier))

{

entieridentifiant1= 1;

entierid2= 2;

entierd'habitude= (*ptr)(id2);

cout<<« fonction principale : »<<identifiant1<<''<<ch<<''<<d'habitude<<' ';

reveniridentifiant1;

}


entiercb(entieridentifier)

{

cout<<'fonction de rappel'<<' ';

reveniridentifier;

}


entierprincipale()

{

entier (*ptr)(entier) = &cb;

carbonisernon[] = 'et';

mainFn(père, cb);



revenir 0;

}

La sortie est :





fonction de rappel

fonction principale: 1 et 2

La fonction principale est identifiée par principalFn(). La fonction de rappel est identifiée par cb(). La fonction de rappel est définie en dehors de la fonction principale mais en réalité appelée au sein de la fonction principale.

Notez la déclaration de la fonction de rappel en tant que paramètre dans la liste des paramètres de la déclaration de la fonction principale. La déclaration de la fonction de rappel est int (*ptr)(int). Notez l'expression de la fonction de rappel, comme un appel de fonction, dans la définition de la fonction principale ; tout argument pour l'appel de la fonction de rappel y est passé. L'instruction pour cet appel de fonction est :



entierd'habitude= (*ptr)(id2);

Où id2 est un argument. ptr fait partie du paramètre, un pointeur, qui sera lié à la référence de la fonction de rappel dans la fonction main().

Notez l'expression :

entier (*ptr)(entier) = &cb;

Dans la fonction main(), qui lie la déclaration (sans définition) de la fonction de rappel au nom de la définition de la même fonction de rappel.

La fonction principale est appelée, dans la fonction main(), comme :

mainFn(père, cb);

Où cha est une chaîne et cb est le nom de la fonction de rappel sans aucun de ses arguments.

Comportement synchrone de la fonction de rappel

Considérez le programme suivant :

#comprendre

à l'aide de espace de nomsles heures;



annulermainFn(annuler (*ptr)())

{

cout<<'fonction principale'<<' ';

(*ptr)();

}


annulercb()

{

cout<<'fonction de rappel'<<' ';

}


annulerfn()

{

cout<<'vu'<<' ';

}


entierprincipale()

{

annuler (*ptr)() = &cb;

mainFn(cb);

fn();



revenir 0;

}

La sortie est :

fonction principale

fonction de rappel

vu

Il y a une nouvelle fonction ici. Tout ce que la nouvelle fonction fait, c'est d'afficher la sortie, vue. Dans la fonction main(), la fonction principale est appelée, puis la nouvelle fonction, fn() est appelée. La sortie montre que le code de la fonction principale a été exécuté, puis celui de la fonction de rappel a été exécuté et enfin celui de la fonction fn() a été exécuté. Il s'agit d'un comportement synchrone (à thread unique).

S'il s'agissait d'un comportement asynchrone, lorsque trois segments de code sont appelés dans l'ordre, le premier segment de code peut être exécuté, suivi à la place de l'exécution du troisième segment de code, avant que le deuxième segment de code ne soit exécuté.

Eh bien, la fonction fn() peut être appelée à partir de la définition de la fonction principale, au lieu de la fonction main(), comme suit :

#comprendre

à l'aide de espace de nomsles heures;



annulerfn()

{

cout<<'vu'<<' ';

}


annulermainFn(annuler (*ptr)())

{

cout<<'fonction principale'<<' ';

fn();

(*ptr)();

}


annulercb()

{

cout<<'fonction de rappel'<<' ';

}


entierprincipale()

{

annuler (*ptr)() = &cb;

mainFn(cb);



revenir 0;

}

La sortie est :

fonction principale

vu

fonction de rappel

Il s'agit d'une imitation du comportement asynchrone. Ce n'est pas un comportement asynchrone. Il s'agit toujours d'un comportement synchrone.

Aussi, l'ordre d'exécution du segment de code de la fonction principale et du segment de code de la fonction de rappel peut être interverti dans la définition de la fonction principale. Le programme suivant illustre cela :

#comprendre

à l'aide de espace de nomsles heures;



annulermainFn(annuler (*ptr)())

{

(*ptr)();

cout<<'fonction principale'<<' ';

}


annulercb()

{

cout<<'fonction de rappel'<<' ';

}


annulerfn()

{

cout<<'vu'<<' ';

}


entierprincipale()

{

annuler (*ptr)() = &cb;

mainFn(cb);

fn();



revenir 0;

}

La sortie est maintenant,

fonction de rappel

fonction principale

vu

C'est aussi une imitation du comportement asynchrone. Ce n'est pas un comportement asynchrone. Il s'agit toujours d'un comportement synchrone. Un véritable comportement asynchrone peut être obtenu comme expliqué dans la section suivante ou avec la bibliothèque future.

Comportement asynchrone avec fonction de rappel

Le pseudo-code du schéma de fonction de rappel asynchrone de base est :

sortie de type;

tapez cb(sortie de type)

{

//déclarations

}


tapez principalFn(tapez entrée, tapez cb(sortie de type))

{

//déclarations

}

Notez les positions des données d'entrée et de sortie aux différents endroits du pseudo-code. L'entrée de la fonction de rappel est sa sortie. Les paramètres de la fonction principale sont le paramètre d'entrée du code général et le paramètre de la fonction de rappel. Avec ce schéma, une troisième fonction peut être exécutée (appelée) dans la fonction main() avant que la sortie de la fonction de rappel ne soit lue (toujours dans la fonction main()). Le code suivant illustre cela :

#comprendre

à l'aide de espace de nomsles heures;

carboniser *sortir;


annulercb(carboniserdehors[])

{

sortir=dehors;

}



annulermainFn(carbonisersaisir[],annuler (*ptr)(carboniser[cinquante]))

{

(*ptr)(saisir);

cout<<'fonction principale'<<' ';

}


annulerfn()

{

cout<<'vu'<<' ';

}


entierprincipale()

{

carbonisersaisir[] = 'fonction de rappel';

annuler (*ptr)(carboniser[]) = &cb;

mainFn(entrée, cb);

fn();

cout<<sortir<<' ';



revenir 0;

}

La sortie du programme est :

fonction principale

vu

fonction de rappel

Dans ce code particulier, la donnée de sortie et la donnée d'entrée se trouvent être la même donnée. Le résultat du troisième appel de fonction dans la fonction main() a été affiché avant le résultat de la fonction de rappel. La fonction de rappel a exécuté, terminé et affecté son résultat (valeur) à la variable, sortie, permettant au programme de continuer sans son interférence. Dans la fonction main(), la sortie de la fonction de rappel était utilisée (lue et affichée) lorsque cela était nécessaire, entraînant un comportement asynchrone pour l'ensemble du schéma.

C'est le moyen monothread d'obtenir un comportement asynchrone de la fonction de rappel avec du C++ pur.

Utilisation de base de la future bibliothèque

L'idée du schéma de fonction de rappel asynchrone est que la fonction principale retourne avant le retour de la fonction de rappel. Cela a été fait indirectement, efficacement, dans le code ci-dessus.

Notez à partir du code ci-dessus que la fonction de rappel reçoit l'entrée principale du code et produit la sortie principale du code. La bibliothèque C++, future, a une fonction appelée sync(). Le premier argument de cette fonction est la référence de la fonction de rappel ; le deuxième argument est l'entrée de la fonction de rappel. La fonction sync() retourne sans attendre la fin de l'exécution de la fonction de rappel mais permet à la fonction de rappel de se terminer. Cela fournit un comportement asynchrone. Alors que la fonction de rappel continue de s'exécuter, puisque la fonction sync() est déjà retournée, les instructions en dessous continuent de s'exécuter. C'est comme un comportement asynchrone idéal.

Le programme ci-dessus a été réécrit ci-dessous, en tenant compte de la future bibliothèque et de sa fonction sync() :

#comprendre

#comprendre

#comprendre

à l'aide de espace de nomsles heures;

futur<chaîne de caractères>sortir;

chaîne cb(chaîne de caractères)

{

revenirstri;

}



annulermainFn(entrée de chaîne)

{

sortir=asynchrone(cb, entrée);

cout<<'fonction principale'<<' ';

}


annulerfn()

{

cout<<'vu'<<' ';

}


entierprincipale()

{

entrée de chaîne=chaîne de caractères('fonction de rappel');

mainFn(saisir);

fn();

ret de chaîne=sortir.avoir(); // attend le retour du rappel si nécessaire

cout<<droit<<' ';



revenir 0;

}

La fonction sync() stocke enfin la sortie de la fonction de rappel dans le futur objet. La sortie attendue peut être obtenue dans la fonction main(), en utilisant la fonction membre get() du futur objet.

Conclusion

Une fonction de rappel est une fonction, qui est un argument, pas un paramètre, dans une autre fonction. Un schéma de fonction de rappel a besoin d'une fonction principale et de la fonction de rappel elle-même. La déclaration de la fonction de rappel fait partie de la liste des paramètres de la fonction principale. La définition de la fonction de rappel est indiquée dans l'appel de fonction de la fonction principale (dans main()). La fonction de rappel est en fait appelée dans la définition de la fonction principale.

Un schéma de fonction de rappel n'est pas nécessairement asynchrone. Pour être sûr que le schéma de la fonction de rappel est asynchrone, effectuez l'entrée principale du code, l'entrée de la fonction de rappel ; faire la sortie principale du code, la sortie de la fonction de rappel ; stocker la sortie de la fonction de rappel dans une variable ou une structure de données. Dans la fonction main(), après avoir appelé la fonction principale, exécutez d'autres instructions de l'application. Lorsque la sortie de la fonction de rappel est nécessaire, dans la fonction main(), utilisez-la (lire et afficher) ici et là.