2009-04-13 8 views
3

Je me demandais s'il y avait une meilleure façon de gérer le cas dans C où vous voulez quitter une fonction dès que vous rencontrez une erreur dans une série d'expressions. (dans ce cas, c'est une fonction qui renvoie une valeur NULL en cas d'erreur)Traitement des erreurs de court-circuit dans C

par exemple. dans un certain code C où ils ont essayé de court-circuiter la gestion des erreurs en combinant une série d'instructions avec AND (& &).

return doSomething() && 
    doSomething2() && 
    doSomething3() && ... ; 

Cela me énerve puisque nous entasser tant sur une ligne dans une déclaration. Mais je suppose que l'alternative est

if (!(value = doSomething())) 
    return NULL; 
if (!(value = doSomething2())) 
    return NULL; 
etc 
return value; 

Mais qu'en est-évaluation des erreurs de court-circuit que je l'ai vu dans les scripts Perl et bash ..

int die(int retvalue) { 
    exit(retvalue); 
} 
..... 
(value = doSomething()) || die(1); 
(value = doSomething2()) || die(2); 
(value = doSomething3()) || die(3); 
return value; 

Le principal problème avec ceci est que l'ERS doit être une expression de sorte que vous ne pouvez pas vraiment aller ou revenir de la fonction. Est-ce que quelqu'un trouverait cela de valeur ou est-ce trop limité?

edit: Je suppose que j'aurais dû inclure des nouvelles lignes dans le premier exemple. Le problème est que vous devez faire attention si vous décidez d'ajouter une autre expression au milieu.

Répondre

5

J'ai vu l'utilisation de GOTO en C dans ce but précis. Comme il n'y a pas de construction 'finally' en C, une méthode était nécessaire pour libérer toute votre mémoire même si vous étiez en train de quitter une fonction plus tôt.

Il est donc essentiellement comme suit (quelqu'un pourrait corriger ma syntaxe C si je me trompe, je suis un peu rouillé)

int foo() 
{ 
    /* Do Stuff */ 
    if(!DoSomething()) 
     GOTO Failure; 

    if(!DoSomething2()) 
     GOTO Failure; 

    if(!DoSomething3()) 
     GOTO Failure; 

    /* return success */ 
    return true; 

    Failure: 
    /* release allocated resources */ 
    /* print to log if necessary */ 
    return false; 
} 

Remarque importante Ne pas utiliser GOTO pour le flux d'exécution. Ils ne devraient être utilisés que sur erreur, et seulement pour aller à la fin de la fonction en cours. Si vous les utilisez pour autre chose, vous créez un code spaghetti qui pourrait détruire le tissu de la réalité. Ne fais pas ça.

EDIT

Comme l'une des affiches a noté, en utilisant la sortie (x) va tuer l'ensemble de votre programme, qui maintient cette solution réservée aux erreurs fatales. Cependant, votre solution originale proposée (DS() & & DS2() & & DS3()) le tout sur une ligne pose un problème pour la gestion des erreurs.

Si vous vouliez inclure les fonctions dans une sorte de gestion d'erreur spécifique à une fonction, il n'y a aucun moyen de le faire lorsque vous encapsulez les appels de fonction sur une seule ligne. Donc, à tout le moins, vous pourriez faire quelque chose comme

int result1 = 0; 
int result2 = 0; 
int result3 = 0; 

result1 = DoSomething(); 

if(result1) 
    result2 = DoSomething2(); 

if(result2) 
    result3 = DoSomething3(); 

return result1 && result2 && result3; 

Parce que cette méthode n'empêcherait pas la gestion des erreurs.

+0

Je ne sais pas pourquoi. Votre réponse était assez raisonnable. –

+0

Juste une note rapide, @devinb, votre édition fait tous les trois qui n'est pas la même que la version court-circuitée dans la question (ou votre original). Bien s'il n'y a pas d'effets secondaires (ou si les performances ne sont pas affectées) mais cela ne peut pas être garanti. – paxdiablo

+0

Merci pax, je l'ai réparé. – DevinB

4

Si votre seule préoccupation est bachotage trop sur une seule ligne, pourquoi ne pas utiliser simplement:

return doSomething() 
    && doSomething2() 
    && doSomething3() 
    && ... ; 

J'ai tendance à écrire le second cas comme:

if (!(value = doSomething())) return NULL; 
if (!(value = doSomething2())) return NULL; 
: : : : : 
return value; 

(même doublure je veux voir autant de code sur un écran qu'une seule fois (et vous pouvez insérer d'autres contrôles entre les lignes si nécessaire).

+0

Cela aide la lisibilité, mais cela n'aide pas le fait que la ligne est surchargée. – DevinB

+0

@devinb, je ne sais pas pourquoi ce fait est en fait un problème. Il y a deux choses que vous devez pouvoir faire avec la source (a) la lire et la comprendre, (b) la compiler. Le compilateur n'a aucun problème avec les deux versions, donc j'ai supposé que c'était irritant parce que c'était difficile à lire. Le reformatage corrige cela. – paxdiablo

+0

Tant qu'une solution est compilée avec les mêmes fonctionnalités et performances, elles sont toutes les mêmes. Certains, comme les solutions de type goto, peuvent être considérés comme moins lisibles (pas par moi puisque je pense comme un ordinateur :-). – paxdiablo

0

Je recommande contre votre technique proposée. Au début, la sortie en C termine le processus; ce n'est pas seulement le retour. Donc, il est limité à seulement quelques cas d'erreurs fatales.

La première solution que vous essayez d'éviter est la meilleure et la plus facile à comprendre de mon point de vue.

1

Que diriez-vous de quelque chose comme ça?

int result; 

result = doSomething(); 

result = result && doSomething2(); 

result = result && doSomething3(); 

return result; 

Il utilise un court-circuit d'une manière similaire à votre premier exemple, mais il vous permet de le casser sur plusieurs lignes et ajouter des commentaires etc.

+0

faire ce résultat && = doSomething2() –

+0

Je le ferais, mais il n'y a pas d'opérateur "&& =" dans C. – GrahamS

+0

En effet. Je m'excuse. –

0

La façon idiomatiques d'écrire cela en C est, dans mon expérience, est de démarrer la fonction avec une série de déclarations if qui vérifient les conditions préalables du reste de la fonction. Si une condition préalable n'est pas remplie, un retour immédiat avec un code d'erreur est effectué. De cette façon, quand vous arrivez à la partie principale du corps de la fonction, vous savez que tout est OK et que vous pouvez garder le code le plus simple possible.

En d'autres termes, vous deuxième approche.

À titre d'exemple, on peut écrire une fonction pour copier une chaîne comme suit:

int copy_string(char *source, char *target, size_t max) 
{ 
    if (source == NULL) 
     return -1; 
    if (target == NULL) 
     return -1; 
    source_len = strlen(source); 
    if (source_len + 1 > max) // +1 for NUL 
     return -1; 
    memcpy(target, source, source_len + 1); 
    return 0; 
} 

(J'aime la convention d'appel système Unix de retour -1 pour les erreurs, mais c'est une question discutable de style et . rien à voir avec cette question)

0

J'ai vu ceci avant:

int Function (void) 
{ 
    int Result = DoSomething(); 

    if (Result) Result = DoSomething2(); 
    if (Result) Result = DoSomething3(); 
    if (Result) Result = DoSeomthing4(); 

    return Result; 
} 

Il semble assez soignée, facilement alignable. Il ne sortira pas tout de suite mais il n'exécutera rien d'autre. Cela suppose bien entendu que les fonctions renvoient non nulles en cas de succès. Sinon, vous pouvez simplement utiliser if (!Result) si les fonctions retournent une valeur non nulle en cas d'échec.

Questions connexes