J'aimerais pouvoir forcer une "double rendement", c'est-à-dire une fonction qui force un retour de la fonction appeler em> (oui, je sais qu'il n'y a pas toujours de vrai Fonction d'appel, etc.) Évidemment, je m'attends à pouvoir le faire en manipulant la pile, et je suppose qu'il est possible au moins d'une manière non portable de la machine à la machine. La question est de savoir si cela peut être fait relativement proprement et de manière portant. Pour donner un morceau de code concret à remplir, je veux écrire la fonction P> de sorte que La fonction suivante p> retourne, disons, Notes: strong> p> 1 code>; et le long calcul n'est pas effectué. Supposons que
foo () code> peut supposer qu'il ne s'appelle que par une fonction avec la signature de la barre, c'est-à-dire un
int (int) code> (et sait donc spécifiquement ce que son type de retour de l'appelant est ). p>
bar () code>) ne doit pas strong> être modifié. Il ne sera pas au courant de ce que la fonction appelée est à la hauteur. (Encore dans l'exemple, seul le bit
/ * magique * / code> peut être modifié). LI>
4 Réponses :
La question est de savoir si cela peut être fait relativement proprement et de manière portante. P>
La réponse est qu'elle ne peut pas. p>
Mis à part tous les détails non portables de la manière dont la pile d'appel est implémentée sur différents systèmes, supposons
FOO code> est inliné dans
bar code>. Ensuite (généralement), il n'aura pas son propre cadre de pile. Vous ne pouvez pas parler de manière proprement ou de manière portant une ingénierie inverse d'une version "double" ou "N-Times", car la pile d'appel réelle ne ressemble pas nécessairement à ce que vous attendez basé sur les appels de l'Abstrait C ou C ++ Machine. P>
Les informations dont vous avez besoin pour pirater ceci sont probablement em> (aucune garantie) disponible avec des informations de débogage. Si un débogueur va présenter la pile d'appels "logique" à son utilisateur, y compris les appels inliner, il doit y avoir suffisamment d'informations disponibles pour localiser l'appelant "Deux niveaux en haut". Ensuite, vous devez imiter le code de sortie de fonction spécifique à la plate-forme pour éviter de ne rien briser. Cela nécessite de restaurer tout ce que la fonction intermédiaire restaurerait normalement, ce qui pourrait ne pas être facile à comprendre même avec des informations de débogage, car le code pour le faire est dans
bar code> quelque part. Mais je soupçonne que, puisque le débogueur peut montrer l'état de cette fonction d'appel, puis au moins en principe, les informations de débogage contiennent probablement suffisamment d'informations pour la restaurer. Revenez ensuite à cet emplacement de l'appelant d'origine (qui pourrait être atteint avec un saut explicite, ou en manipulant partout où votre plate-forme conserve son adresse de retour et effectue un rendement normal). Tout cela est très sale et très non portable, d'où ma réponse "non". P>
Je suppose que vous savez déjà que vous pouvez utiliser des exceptions ou
setJMP code> /
/
LONDJMP code>.
bar code> ou l'appelant de
bar code> (ou les deux) nécessiterait de coopérer avec cela et d'accord avec
foo code> comment la "valeur de retour> " est stocké. Alors je suppose que ce n'est pas ce que tu veux. Mais si la modification de l'appelant de
bar code> est acceptable, vous pouvez faire quelque chose comme ça. Ce n'est pas joli, mais il suffit de fonctionner (en C ++ 11, en utilisant des exceptions). Je laisserai cela pour savoir comment comprendre comment le faire en c utilise
setingjmp code> /
longjmp code> et avec une signature de fonction fixe au lieu d'un modèle: p> < Pré> xxx pré>
Enfin, pas une "conférence de mauvaise pratique", mais un avertissement pratique - en utilisant tout astuce em> pour retourner tôt ne va pas en général avec le code écrit en C ou écrit en C ++ qui ne s'attend pas à une exception à laisser
foo code>. La raison est que
bar code> aurait pu attribuer certaines ressources ou mettre une certaine structure dans un état qui enfreint ses invariants avant d'appeler
foo code>, avec l'intention de libérer cette ressource ou de restaurer la invariant dans le code suivant l'appel. Donc, pour les fonctions générales code> bar code>, si vous ignorez le code dans
bar code>, vous pouvez provoquer une fuite de mémoire ou un état de données non valide. Le seul moyen d'éviter cela en général, peu importe ce qui est dans
bar code>, consiste à autoriser le reste de la barre code> code> à exécuter. Bien sûr, si
bar code> est écrit en C ++ avec l'attente que
foo code> pourrait lancer, il aura utilisé RAII pour le code de nettoyage et il s'exécutera lorsque vous lancez.
LONDJMP CODE> ING Over ADESTRUCTOR a un comportement non défini, cependant, vous devez donc décider avant de commencer si vous avez affaire à C ++ ou avec c. P> blockQuote>
Le seul moyen propre est de modifier vos fonctions:
bool foo(int x) { if (skip) return true; return false; } int bar(int x) { if (foo(x)) return 1; /* long computation here */ return 0; }
@Einpoklum: Alors évidemment, tu es assez vissé. :) Un sous-programme ne peut pas se contrôler de son appelant de manière fiable et de manière géniale avec ce type de précision. (Les quelques-uns qui semblent, soit de finir par dos i> à l'appelant à un moment donné, soit tuez le processus afin que le messentiré soit discuté.) Vous devez savoir comment l'appelant fonctionne et si < CODE> BAR CODE> Peut être une fonction arbitraire qui prend et renvoie un int, vous ne savez pas comment la pile est aménagée.
Il existe deux manières portables de le faire, mais les deux nécessitent une assistance de la fonction de l'appelant. Pour C, c'est SETJMP + LONDJMP. Pour C ++, il s'agit d'une utilisation des exceptions (essayer + attraper + lancer). Les deux sont assez similaires dans la mise en œuvre (en substance, une implémentation d'exceptions précoce était basée sur SETJMP). Et, il n'y a certainement pas de moyen portable de faire cela sans la sensibilisation à la fonction de l'appelant ... P>
Modifier votre fonction vide FOO () code> Pour renvoyer un Booléen OUI / NON, puis enveloppez-le dans une macro du même nom:
extern bool (foo)(int);
Ce n'est pas seulement une mauvaise pratique; Ce n'est pas possible sans modifier
bar code> également ou faire des tours stupides avec des exceptions. Ce genre de chose est entièrement ce que le mouvement de programmation structuré se battait contre (raisonnablement et avec succès, je devrais ajouter).
Comment la fonction interne est-elle censée savoir quel est le type de retour de l'appelant ?!
"Relativement proprement et de manière significative" est très subjectif. Voter pour fermer comme base d'opinion.
@Kerreksb: Cela découle de l'hypothèse concernant le type de l'appelant. Je l'ai rendue plus explicite maintenant.