Essayez les instructions catch en C

101

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

Andrew
la source
3
Les mécanismes de type exception ne seront généralement pas utiles sans un mécanisme pour libérer automatiquement des ressources lorsque la pile est déroulée. C ++ utilise RAII; Java, C #, Python, etc. utilisent des garbage collector. (Et notez que les garbage collector ne libèrent que de la mémoire. Pour libérer automatiquement d'autres types de ressources, ils ajoutent également des éléments tels que des finaliseurs ou des gestionnaires de contexte ...)
jamesdlin
@jamesdlin, Pourquoi ne pourrions-nous pas faire RAII avec C?
Pacerier
1
@Pacerier RAII nécessite d'appeler automatiquement des fonctions lorsque des objets sont détruits (c'est-à-dire des destructeurs). Comment proposez-vous de faire cela en C?
jamesdlin

Réponses:

90

C lui-même ne prend pas en charge les exceptions, mais vous pouvez les simuler dans une certaine mesure avec les appels setjmpet longjmp.

static jmp_buf s_jumpBuffer;

void Example() { 
  if (setjmp(s_jumpBuffer)) {
    // The longjmp was executed and returned control here
    printf("Exception happened here\n");
  } else {
    // Normal code execution starts here
    Test();
  }
}

void Test() {
  // Rough equivalent of `throw`
  longjmp(s_jumpBuffer, 42);
}

Ce site Web a un joli tutoriel sur la façon de simuler des exceptions avec setjmpetlongjmp

JaredPar
la source
1
solution géniale! est cette solution croisée? Cela a fonctionné pour moi sur MSVC2012 mais pas dans le compilateur MacOSX Clang.
mannysz le
1
indiquez-moi: je pensais que les clauses try catch vous permettaient d'attraper des exceptions (comme la division par zéro). Cette fonction semble vous permettre uniquement d'attraper les exceptions que vous lancez vous-même. Les vraies exceptions ne sont pas levées en appelant longjmp, non? Si j'utilise ce code pour faire quelque chose comme try{ x = 7 / 0; } catch(divideByZeroException) {print('divided by zero')}; ça ne fonctionnera pas correctement?
Sam le
L'écart par zéro n'est même pas une exception en C ++, pour le gérer, vous devez soit vérifier que le diviseur n'est pas égal à zéro et le gérer, soit gérer le SIGFPE qui est levé lorsque vous exécutez une formule écart par zéro.
James
25

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.

Alok Save
la source
3
@JensGustedt C'est exactement ce pour quoi goto est actuellement utilisé très souvent et par exemple là où cela a du sens (setjmp / ljmp est une meilleure alternative, mais label + goto est généralement utilisé plus).
Tomas Pruzina
1
@AoeAoe, gotoest 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. goton'est pas un équivalent pour try / catch car il est limité à la même fonction.
Jens Gustedt
@JensGustedt J'ai en quelque sorte réagi à la haine / peur de goto et des gens qui l'utilisent (mes professeurs m'ont également raconté des histoires effrayantes d'utilisation de goto à l'université). [OT] La seule chose qui est vraiment, vraiment risquée et «trouble» à propos de goto est de «revenir en arrière», mais j'ai vu cela dans Linux VFS (git blame guy jurait que c'était une performance critique-bénéfique).
Tomas Pruzina
Voir les sources systemctl pour les utilisations légitimes de gotocomme mécanisme try / catch utilisé dans une source moderne, largement acceptée et examinée par des pairs. Recherchez gotoun équivalent "throw" et finishun équivalent "catch".
Stewart
13

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 utiliser abusivement 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)

#include <stdbool.h>

#define try bool __HadError=false;
#define catch(x) ExitJmp:if(__HadError)
#define throw(x) __HadError=true;goto ExitJmp;

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:

#include <stdio.h>
#include <stdbool.h>

#define try bool __HadError=false;
#define catch(x) ExitJmp:if(__HadError)
#define throw(x) __HadError=true;goto ExitJmp;

int main(void)
{
    try
    {
        printf("One\n");
        throw();
        printf("Two\n");
    }
    catch(...)
    {
        printf("Error\n");
    }
    return 0;
}

Cela fonctionne à quelque chose comme:

int main(void)
{
    bool HadError=false;
    {
        printf("One\n");
        HadError=true;
        goto ExitJmp;
        printf("Two\n");
    }
ExitJmp:
    if(HadError)
    {
        printf("Error\n");
    }
    return 0;
}

Version 2 (saut de portée)

#include <stdbool.h>
#include <setjmp.h>

jmp_buf *g__ActiveBuf;

#define try jmp_buf __LocalJmpBuff;jmp_buf *__OldActiveBuf=g__ActiveBuf;bool __WasThrown=false;g__ActiveBuf=&__LocalJmpBuff;if(setjmp(__LocalJmpBuff)){__WasThrown=true;}else
#define catch(x) g__ActiveBuf=__OldActiveBuf;if(__WasThrown)
#define throw(x) longjmp(*g__ActiveBuf,1);

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é:

jmp_buf *g_ActiveBuf;

int main(void)
{
    jmp_buf LocalJmpBuff;
    jmp_buf *OldActiveBuf=g_ActiveBuf;
    bool WasThrown=false;
    g_ActiveBuf=&LocalJmpBuff;

    if(setjmp(LocalJmpBuff))
    {
        WasThrown=true;
    }
    else
    {
        printf("One\n");
        longjmp(*g_ActiveBuf,1);
        printf("Two\n");
    }
    g_ActiveBuf=OldActiveBuf;
    if(WasThrown)
    {
        printf("Error\n");
    }
    return 0;
}

Cela utilise un pointeur global pour que longjmp () sache quel essai a été exécuté en dernier. Nous utilisons abusivement 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):

  • Il ne libérera pas la mémoire allouée car aucun déconstructeur n'est appelé.
  • Vous ne pouvez pas avoir plus d'un essai / capture dans une portée (pas d'imbrication)
  • Vous ne pouvez pas réellement lancer d'exceptions ou d'autres données comme en C ++
  • Pas du tout thread-safe
  • Vous configurez d'autres programmeurs en cas d'échec car ils ne remarqueront probablement pas le piratage et essaieront de les utiliser comme des blocs try / catch C ++.
Paul Hutchinson
la source
belles solutions alternatives.
HaseeB Mir
la version 1 est une bonne idée, mais cette variable __HadError devrait être réinitialisée ou étendue. Sinon, vous ne pourrez pas utiliser plus d'un try-catch dans le même bloc. Peut-être utiliser une fonction globale comme 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.
flamewave000
Oui, il est limité à un try-catch dans la même fonction. Cependant, un plus gros problème que la variable est l'étiquette car vous ne pouvez pas avoir d'étiquettes en double dans la même fonction.
Paul Hutchinson
10

Dans C99, vous pouvez utiliser setjmp/ longjmppour 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.

Kerrek SB
la source
5

Alors que certaines des autres réponses ont couvert les cas simples d'utilisation setjmpet longjmp, dans une application réelle, il y a deux préoccupations qui comptent vraiment.

  1. Imbrication de blocs try / catch. L'utilisation d'une seule variable globale pour vous jmp_bufles empêchera de fonctionner.
  2. Filetage. Une seule variable globale pour vous jmp_bufcausera toutes sortes de douleurs dans cette situation.

La solution à ces problèmes consiste à maintenir une pile locale de thread jmp_bufqui 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)

static jmp_buf s_jumpBuffer;

void Example() { 
  if (setjmp(s_jumpBuffer)) {
    // The longjmp was executed and returned control here
    printf("Exception happened\n");
  } else {
    // Normal code execution starts here
    Test();
  }
}

void Test() {
  // Rough equivalent of `throw`
  longjump(s_jumpBuffer, 42);
}

Vous utiliseriez quelque chose comme:

#define MAX_EXCEPTION_DEPTH 10;
struct exception_state {
  jmp_buf s_jumpBuffer[MAX_EXCEPTION_DEPTH];
  int current_depth;
};

int try_point(struct exception_state * state) {
  if(current_depth==MAX_EXCEPTION_DEPTH) {
     abort();
  }
  int ok = setjmp(state->jumpBuffer[state->current_depth]);
  if(ok) {
    state->current_depth++;
  } else {
    //We've had an exception update the stack.
    state->current_depth--;
  }
  return ok;
}

void throw_exception(struct exception_state * state) {
  longjump(state->current_depth-1,1);
}

void catch_point(struct exception_state * state) {
    state->current_depth--;
}

void end_try_point(struct exception_state * state) {
    state->current_depth--;
}

__thread struct exception_state g_exception_state; 

void Example() { 
  if (try_point(&g_exception_state)) {
    catch_point(&g_exception_state);
    printf("Exception happened\n");
  } else {
    // Normal code execution starts here
    Test();
    end_try_point(&g_exception_state);
  }
}

void Test() {
  // Rough equivalent of `throw`
  throw_exception(g_exception_state);
}

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 de MAX_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;)

Michael Anderson
la source
4

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 :)

James Adam
la source
4

Cela peut être fait avec setjmp/longjmpen 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.

Jens Gustedt
la source
2

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:

GameEngine *CreateGameEngine(GameEngineParams const *params)
{
    /* Declare an error handler variable. This will hold the address
       to jump to if an error occurs to cleanup pending resources.
       Initialize it to the err label which simply returns an
       error value (NULL in this example). The && operator resolves to
       the address of the label err */
    void *eh = &&err;

    /* Try the allocation */
    GameEngine *engine = malloc(sizeof *engine);
    if (!engine)
        goto *eh; /* this is essentially your "throw" */

    /* Now make sure that if we throw from this point on, the memory
       gets deallocated. As a convention you could name the label "undo_"
       followed by the operation to rollback. */
    eh = &&undo_malloc;

    /* Now carry on with the initialization. */
    engine->window = OpenWindow(...);
    if (!engine->window)
        goto *eh;   /* The neat trick about using approach is that you don't
                       need to remember what "undo" label to go to in code.
                       Simply go to *eh. */

    eh = &&undo_window_open;

    /* etc */

    /* Everything went well, just return the device. */
    return device;

    /* After the return, insert your cleanup code in reverse order. */
undo_window_open: CloseWindow(engine->window);
undo_malloc: free(engine);
err: return NULL;
}

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.

/* Put at the beginning of a function that may fail. */
#define declthrows void *_eh = &&err

/* Cleans up resources and returns error result. */
#define throw goto *_eh

/* Sets a new undo checkpoint. */
#define undo(label) _eh = &&undo_##label

/* Throws if [condition] evaluates to false. */
#define check(condition) if (!(condition)) throw

/* Throws if [condition] evaluates to false. Then sets a new undo checkpoint. */
#define checkpoint(label, condition) { check(condition); undo(label); }

Alors l'exemple devient

GameEngine *CreateGameEngine(GameEngineParams const *params)
{
    declthrows;

    /* Try the allocation */
    GameEngine *engine = malloc(sizeof *engine);
    checkpoint(malloc, engine);

    /* Now carry on with the initialization. */
    engine->window = OpenWindow(...);
    checkpoint(window_open, engine->window);

    /* etc */

    /* Everything went well, just return the device. */
    return device;

    /* After the return, insert your cleanup code in reverse order. */
undo_window_open: CloseWindow(engine->window);
undo_malloc: free(engine);
err: return NULL;
}
Keebus
la source
2

Attention: ce qui suit n'est pas très sympa mais ça fait le boulot.

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    unsigned int  id;
    char         *name;
    char         *msg;
} error;

#define _printerr(e, s, ...) fprintf(stderr, "\033[1m\033[37m" "%s:%d: " "\033[1m\033[31m" e ":" "\033[1m\033[37m" " ‘%s_error’ " "\033[0m" s "\n", __FILE__, __LINE__, (*__err)->name, ##__VA_ARGS__)
#define printerr(s, ...) _printerr("error", s, ##__VA_ARGS__)
#define printuncaughterr() _printerr("uncaught error", "%s", (*__err)->msg)

#define _errordef(n, _id) \
error* new_##n##_error_msg(char* msg) { \
    error* self = malloc(sizeof(error)); \
    self->id = _id; \
    self->name = #n; \
    self->msg = msg; \
    return self; \
} \
error* new_##n##_error() { return new_##n##_error_msg(""); }

#define errordef(n) _errordef(n, __COUNTER__ +1)

#define try(try_block, err, err_name, catch_block) { \
    error * err_name = NULL; \
    error ** __err = & err_name; \
    void __try_fn() try_block \
    __try_fn(); \
    void __catch_fn() { \
        if (err_name == NULL) return; \
        unsigned int __##err_name##_id = new_##err##_error()->id; \
        if (__##err_name##_id != 0 && __##err_name##_id != err_name->id) \
            printuncaughterr(); \
        else if (__##err_name##_id != 0 || __##err_name##_id != err_name->id) \
            catch_block \
    } \
    __catch_fn(); \
}

#define throw(e) { *__err = e; return; }

_errordef(any, 0)

Usage:

errordef(my_err1)
errordef(my_err2)

try ({
    printf("Helloo\n");
    throw(new_my_err1_error_msg("hiiiii!"));
    printf("This will not be printed!\n");
}, /*catch*/ any, e, {
    printf("My lovely error: %s %s\n", e->name, e->msg);
})

printf("\n");

try ({
    printf("Helloo\n");
    throw(new_my_err2_error_msg("my msg!"));
    printf("This will not be printed!\n");
}, /*catch*/ my_err2, e, {
    printerr("%s", e->msg);
})

printf("\n");

try ({
    printf("Helloo\n");
    throw(new_my_err1_error());
    printf("This will not be printed!\n");
}, /*catch*/ my_err2, e, {
    printf("Catch %s if you can!\n", e->name);
})

Production:

Helloo
My lovely error: my_err1 hiiiii!

Helloo
/home/naheel/Desktop/aa.c:28: error: my_err2_error my msg!

Helloo
/home/naheel/Desktop/aa.c:38: uncaught error: my_err1_error 

Gardez à l'esprit que cela utilise des fonctions imbriquées et __COUNTER__. Vous serez du bon côté si vous utilisez gcc.

Naheel
la source
1

Redis utilise goto pour simuler try / catch, à mon humble avis, il est très propre et élégant:

/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success. */
int rdbSave(char *filename) {
    char tmpfile[256];
    FILE *fp;
    rio rdb;
    int error = 0;

    snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
    fp = fopen(tmpfile,"w");
    if (!fp) {
        redisLog(REDIS_WARNING, "Failed opening .rdb for saving: %s",
            strerror(errno));
        return REDIS_ERR;
    }

    rioInitWithFile(&rdb,fp);
    if (rdbSaveRio(&rdb,&error) == REDIS_ERR) {
        errno = error;
        goto werr;
    }

    /* Make sure data will not remain on the OS's output buffers */
    if (fflush(fp) == EOF) goto werr;
    if (fsync(fileno(fp)) == -1) goto werr;
    if (fclose(fp) == EOF) goto werr;

    /* Use RENAME to make sure the DB file is changed atomically only
     * if the generate DB file is ok. */
    if (rename(tmpfile,filename) == -1) {
        redisLog(REDIS_WARNING,"Error moving temp DB file on the final destination: %s", strerror(errno));
        unlink(tmpfile);
        return REDIS_ERR;
    }
    redisLog(REDIS_NOTICE,"DB saved on disk");
    server.dirty = 0;
    server.lastsave = time(NULL);
    server.lastbgsave_status = REDIS_OK;
    return REDIS_OK;

werr:
    fclose(fp);
    unlink(tmpfile);
    redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno));
    return REDIS_ERR;
}
Forrest Ye
la source
Le code est cassé. errnone doit être utilisé qu'après l'échec de l'appel système et non trois appels plus tard.
ceving le
Ce code duplique la logique de gestion des erreurs à plusieurs endroits et peut faire des choses incorrectes comme appeler fclose (fp) plusieurs fois. Il serait de loin préférable d'utiliser plusieurs étiquettes et d'encoder ce qui doit encore être récupéré à l'aide de ces étiquettes (plutôt qu'une seule pour toutes les erreurs), puis de sauter au bon emplacement de gestion des erreurs en fonction de l'emplacement du code dans lequel l'erreur se produit.
jschultz410
1

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):

#include <assert.h>

typedef int errcode;

errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
    errcode ret = 0;

    if ( ( ret = foo_init( f ) ) )
        goto FAIL;

    if ( ( ret = goo_init( g ) ) )
        goto FAIL_F;

    if ( ( ret = poo_init( p ) ) )
        goto FAIL_G;

    if ( ( ret = loo_init( l ) ) )
        goto FAIL_P;

    assert( 0 == ret );
    goto END;

    /* error handling and return */

    /* Note that we finalize in opposite order of initialization because we are unwinding a *STACK* of initialized objects */

FAIL_P:
    poo_fini( p );

FAIL_G:
    goo_fini( g );

FAIL_F:
    foo_fini( f );

FAIL:
    assert( 0 != ret );

END:
    return ret;        
}

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:

#include <assert.h>
#include <stdio.h>
#include <string.h>

#define TRY( X, LABEL ) do { if ( ( X ) ) { fprintf( stderr, "%s:%d: Statement '" #X "' failed! %d, %s\n", __FILE__, __LINE__, ret, strerror( ret ) ); goto LABEL; } while ( 0 )

typedef int errcode;

errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
    errcode ret = 0;

    TRY( ret = foo_init( f ), FAIL );
    TRY( ret = goo_init( g ), FAIL_F );
    TRY( ret = poo_init( p ), FAIL_G );
    TRY( ret = loo_init( l ), FAIL_P );

    assert( 0 == ret );
    goto END;

    /* error handling and return */

FAIL_P:
    poo_fini( p );

FAIL_G:
    goo_fini( g );

FAIL_F:
    foo_fini( f );

FAIL:
    assert( 0 != ret );

END:
    return ret;        
}

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.

jschultz410
la source
1
Très agréable. J'ai tendance à faire quelque chose de similaire. goto est idéal pour ce scénario. La seule différence est que je ne vois pas la nécessité de ce dernier "goto END", j'insère simplement un retour de réussite à ce stade, un retour d'échec après le reste.
Neil Roy
1
Merci @NeilRoy La raison du goto END est que j'aime que la grande majorité de mes fonctions aient un seul point d'entrée et un seul point de sortie. De cette façon, si je veux ajouter une logique "enfin" à n'importe quelle fonction, je peux toujours facilement sans avoir à m'inquiéter, il y a d'autres retours cachés qui se cachent quelque part. :)
jschultz410
0

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()et longjmp(), jetez un œil à cette gestion des exceptions de la bibliothèque pjsip, elle fournit sa propre implémentation

onmyway133
la source
-1

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.

mappo
la source
2
Salut mappo, bienvenue dans StackOverflow. Bien qu'intéressante, la question portait spécifiquement sur le faire en C. Donc cela ne répond pas vraiment à la question.
luser droog