Je pensais aujourd'hui aux blocs try / catch existant dans d'autres langues. Googlé pendant un certain temps, mais sans résultat. D'après ce que je sais, il n'y a pas une chose telle que try / catch en C. Cependant, y a-t-il un moyen de les «simuler»?
Bien sûr, il y a assert et d'autres astuces, mais rien de tel que try / catch, qui intercepte également l'exception levée. Je vous remercie
101
Réponses:
C lui-même ne prend pas en charge les exceptions, mais vous pouvez les simuler dans une certaine mesure avec les appels
setjmp
etlongjmp
.Ce site Web a un joli tutoriel sur la façon de simuler des exceptions avec
setjmp
etlongjmp
la source
try{ x = 7 / 0; } catch(divideByZeroException) {print('divided by zero')};
ça ne fonctionnera pas correctement?Vous utilisez goto en C pour des situations similaires de gestion des erreurs.
C'est l'équivalent le plus proche des exceptions que vous pouvez obtenir en C.
la source
goto
est probablement plus utilisé pour la gestion des erreurs, mais alors? La question ne concerne pas la gestion des erreurs en tant que telle, mais explicitement les équivalents try / catch.goto
n'est pas un équivalent pour try / catch car il est limité à la même fonction.goto
comme mécanisme try / catch utilisé dans une source moderne, largement acceptée et examinée par des pairs. Recherchezgoto
un équivalent "throw" etfinish
un équivalent "catch".Ok, je n'ai pas pu résister à cette réponse. Permettez-moi d'abord de dire que je ne pense pas que ce soit une bonne idée de simuler cela en C car c'est vraiment un concept étranger à C.
Nous pouvons
utiliserabusivement le préprocesseur et les variables de la pile locale pour donner une version limitée de C ++ try / throw / catch.Version 1 (portée locale)
La version 1 est un jet local uniquement (ne peut pas quitter la portée de la fonction). Il repose sur la capacité de C99 à déclarer des variables dans le code (cela devrait fonctionner en C89 si l'essai est la première chose dans la fonction).
Cette fonction crée simplement un var local pour savoir s'il y a eu une erreur et utilise un goto pour sauter au bloc catch.
Par exemple:
Cela fonctionne à quelque chose comme:
Version 2 (saut de portée)
La version 2 est beaucoup plus complexe mais fonctionne essentiellement de la même manière. Il utilise un saut en longueur de la fonction en cours vers le bloc try. Le bloc try utilise ensuite un if / else pour sauter le bloc de code vers le bloc catch qui vérifie la variable locale pour voir si elle doit attraper.
L'exemple s'est à nouveau développé:
Cela utilise un pointeur global pour que longjmp () sache quel essai a été exécuté en dernier. Nous
utilisonsabusivement la pile pour que les fonctions enfants puissent également avoir un bloc try / catch.L'utilisation de ce code présente un certain nombre d'inconvénients (mais c'est un exercice mental amusant):
la source
bool __ErrorCheck(bool &e){bool _e = e;e=false;return _e;}
. Mais la variable locale serait également redéfinie, de sorte que les choses deviennent un peu incontrôlables.Dans C99,vous pouvez utilisersetjmp
/longjmp
pour un flux de contrôle non local.Dans une seule portée, le modèle de codage générique et structuré pour C en présence d'allocations de ressources multiples et de sorties multiples est utilisé
goto
, comme dans cet exemple . Ceci est similaire à la façon dont C ++ implémente les appels de destructeurs d'objets automatiques sous le capot, et si vous vous en tenez à cela avec diligence, cela devrait vous permettre un certain degré de propreté même dans les fonctions complexes.la source
Alors que certaines des autres réponses ont couvert les cas simples d'utilisation
setjmp
etlongjmp
, dans une application réelle, il y a deux préoccupations qui comptent vraiment.jmp_buf
les empêchera de fonctionner.jmp_buf
causera toutes sortes de douleurs dans cette situation.La solution à ces problèmes consiste à maintenir une pile locale de thread
jmp_buf
qui est mise à jour au fur et à mesure. (Je pense que c'est ce que lua utilise en interne).Donc au lieu de ça (de la réponse géniale de JaredPar)
Vous utiliseriez quelque chose comme:
Encore une fois, une version plus réaliste de ceci inclurait un moyen de stocker les informations d'erreur dans le
exception_state
, une meilleure gestion deMAX_EXCEPTION_DEPTH
(peut-être en utilisant realloc pour agrandir la mémoire tampon, ou quelque chose comme ça).AVERTISSEMENT: Le code ci-dessus a été écrit sans aucun test. C'est purement pour que vous ayez une idée de la façon de structurer les choses. Différents systèmes et différents compilateurs devront implémenter le stockage local des threads différemment. Le code contient probablement à la fois des erreurs de compilation et des erreurs de logique - donc, pendant que vous êtes libre de l'utiliser comme vous le souhaitez, TESTEZ-le avant de l'utiliser;)
la source
Une recherche rapide sur Google donne des solutions kludgey comme celle- ci qui utilisent setjmp / longjmp comme d'autres l'ont mentionné. Rien d'aussi simple et élégant que le try / catch de C ++ / Java. Je suis plutôt favorable à la gestion des exceptions d'Ada moi-même.
Vérifiez tout avec des instructions if :)
la source
Cela peut être fait avec
setjmp/longjmp
en C. P99 a un ensemble d'outils assez confortable pour cela qui est également cohérent avec le nouveau modèle de filetage de C11.la source
C'est une autre façon de gérer les erreurs en C qui est plus performante que d'utiliser setjmp / longjmp. Malheureusement, cela ne fonctionnera pas avec MSVC, mais si vous n'utilisez que GCC / Clang, vous pouvez l'envisager. Plus précisément, il utilise l'extension "label as value", qui vous permet de prendre l'adresse d'une étiquette, de la stocker dans une valeur et d'y accéder sans condition. Je vais le présenter à l'aide d'un exemple:
Si vous le souhaitez, vous pouvez refactoriser le code commun dans les définitions, implémentant efficacement votre propre système de gestion des erreurs.
Alors l'exemple devient
la source
Attention: ce qui suit n'est pas très sympa mais ça fait le boulot.
Usage:
Production:
Gardez à l'esprit que cela utilise des fonctions imbriquées et
__COUNTER__
. Vous serez du bon côté si vous utilisez gcc.la source
Redis utilise goto pour simuler try / catch, à mon humble avis, il est très propre et élégant:
la source
errno
ne doit être utilisé qu'après l'échec de l'appel système et non trois appels plus tard.En C, vous pouvez "simuler" des exceptions avec une "récupération d'objets" automatique grâce à l'utilisation manuelle de if + goto pour une gestion explicite des erreurs.
J'écris souvent du code C comme le suivant (résumé pour mettre en évidence la gestion des erreurs):
Il s'agit d'un ANSI C complètement standard, sépare la gestion des erreurs de votre code principal, permet le déroulement (manuel) de la pile d'objets initialisés un peu comme le fait C ++, et ce qui se passe ici est tout à fait évident. Étant donné que vous testez explicitement l'échec à chaque point, il est plus facile d'insérer une journalisation spécifique ou une gestion des erreurs à chaque endroit où une erreur peut se produire.
Si cela ne vous dérange pas un peu de magie des macros, vous pouvez rendre cela plus concis tout en faisant d'autres choses comme la journalisation des erreurs avec des traces de pile. Par exemple:
Bien sûr, ce n'est pas aussi élégant que les exceptions C ++ + destructeurs. Par exemple, imbriquer plusieurs piles de gestion des erreurs dans une fonction de cette manière n'est pas très propre. Au lieu de cela, vous voudrez probablement les diviser en sous-fonctions autonomes qui gèrent de la même manière les erreurs, initialiser + finaliser explicitement comme ceci.
Cela ne fonctionne également que dans une seule fonction et ne continuera pas à sauter dans la pile à moins que les appelants de niveau supérieur n'implémentent une logique de gestion des erreurs explicite similaire, alors qu'une exception C ++ continuera à sauter dans la pile jusqu'à ce qu'elle trouve un gestionnaire approprié. Il ne vous permet pas non plus de lancer un type arbitraire, mais uniquement un code d'erreur.
Le codage systématique de cette façon (c'est-à-dire avec une seule entrée et un seul point de sortie) rend également très facile l'insertion d'une logique pré et post ("enfin") qui s'exécutera quoi qu'il arrive. Vous venez de mettre votre logique «enfin» après l'étiquette END.
la source
Si vous utilisez C avec Win32, vous pouvez tirer parti de sa gestion structurée des exceptions (SEH) pour simuler try / catch.
Si vous utilisez C dans des plates-formes qui ne prennent pas en charge
setjmp()
etlongjmp()
, jetez un œil à cette gestion des exceptions de la bibliothèque pjsip, elle fournit sa propre implémentationla source
Peut-être pas un langage majeur (malheureusement), mais en APL, il y a l'opération ⎕EA (signifie Execute Alternate).
Utilisation: 'Y' ⎕EA 'X' où X et Y sont des extraits de code fournis sous forme de chaînes ou de noms de fonctions.
Si X rencontre une erreur, Y (généralement la gestion des erreurs) sera exécuté à la place.
la source