176 votes

Gestion des erreurs dans le code C

Que considérez-vous comme "meilleure pratique" quand il s'agit de la gestion des erreurs les erreurs de manière cohérente dans une bibliothèque C.

Il y a deux façons j'ai pensé:

Toujours renvoyer le code d'erreur. Une fonction typique pourrait ressembler à ceci:

MYAPI_ERROR getObjectSize(MYAPIHandle h, int* returnedSize);

Le toujours prévoir une erreur de pointeur approche:

int getObjectSize(MYAPIHandle h, MYAPI_ERROR* returnedError);

Lors de l'utilisation de la première approche, il est possible d'écrire un code comme celui-ci où l'erreur de manipulation de vérification est directement placé sur l'appel de la fonction:

int size;
if(getObjectSize(h, &size) != MYAPI_SUCCESS) {
  // Error handling
}

Qui semble mieux que le code de gestion d'erreur ici.

MYAPIError error;
int size;
size = getObjectSize(h, &error);
if(error != MYAPI_SUCCESS) {
    // Error handling
}

Cependant, je pense que l'aide de la valeur de retour pour renvoyer des données rend le code plus lisible, Il est évident que quelque chose a été écrit à la taille de la variable dans le deuxième exemple.

Avez-vous des idées sur pourquoi je devrais préférer l'une de ces approches, ou peut-être de les mélanger et de les utiliser de quelque chose d'autre? Je ne suis pas un fan de l'erreur globale unis, car il tend à faire de multi threaded utilisation de la bibliothèque de façon de plus en plus douloureux.

EDIT: C++ des idées précises sur ce serait intéressant de les entendre parler tant qu'ils ne sont pas prévoyant des exceptions puisque ce n'est pas une option pour moi en ce moment...

97voto

AShelly Points 17389

J'ai utilisé les deux approches, et ils ont tous deux travaillé très bien pour moi. Celle que j'utilise, j'ai toujours essayer d'appliquer ce principe:

Si les seules erreurs possibles sont programmeur erreurs, de ne pas retourner un code d'erreur, utilisez affirme à l'intérieur de la fonction.

Une affirmation qui valide les intrants de communiquer clairement ce que la fonction attend, tandis que trop de contrôle d'erreur peut obscurcir la logique du programme. Décider quoi faire pour tous les différents cas d'erreur peut vraiment compliquer la conception. Pourquoi comprendre comment functionX doit manipuler un pointeur null si la place, vous pouvez insister pour que le programmeur n'a jamais passer?

81voto

Nils Pipenbrinck Points 41006

J'aime l'erreur étant donné que le retour de la valeur. Si vous êtes à la conception de l'api et vous souhaitez faire usage de votre bibliothèque aussi indolore que possible de penser à ces ajouts:

  • magasin d'erreur possible-unis dans un typedef ed enum et à utiliser dans votre lib. Ne vous contentez pas de retour ints ou même pire, mélanger ints ou différentes énumérations avec le retour des codes.

  • fournir une fonction qui convertit les erreurs en quelque chose de lisible par l'homme. Peut être simple. Juste erreur enum, const char*.

  • Je sais que cette idée ne fait multithread utiliser un peu difficile, mais ce serait bien si l'application programmeur peut définir un mondial des erreurs de rappel. De cette façon, ils seront en mesure de mettre un point d'arrêt dans la fonction de rappel au cours de bug chasse aux séances.

Espérons que cela aide.

20voto

Jeffrey Cohen Points 191

J'utilise la première approche chaque fois que je créer une bibliothèque. Il y a plusieurs avantages à utiliser un typedef ed enum comme un code de retour.

  • Si la fonction retourne une plus compliqué de sortie comme un tableau et c'est la longueur que vous n'avez pas besoin de créer des structures arbitraires de retour.

    rc = func(..., int **return_array, size_t *array_length);
    
  • Il permet simples, standardisés erreur de manipulation.

    if ((rc = func(...)) != API_SUCCESS) {
       /* Error Handling */
    }
    
  • Il permet, par simple erreur de manipulation dans une fonction de la bibliothèque.

    /* Check for valid arguments */
    if (NULL == return_array || NULL == array_length)
        return API_INVALID_ARGS;
    
  • À l'aide d'un typedef ed enum permet également pour les enum nom pour être visible dans le débogueur. Cela permet pour le débogage plus facile sans la nécessité de consulter en permanence un fichier d'en-tête. Ayant une fonction de traduire cette enum dans une chaîne de caractères est utile aussi bien.

La question la plus importante quelle que soit l'approche utilisée est d'être cohérent. Cela s'applique à la fonction et argument de nommage, l'argument de la commande et la gestion des erreurs.

7voto

Amir Saniyan Points 2406

Utilisation setjmp.

http://en.wikipedia.org/wiki/Setjmp.h

http://aszt.inf.elte.hu/~gsd/halado_cpp/ch02s03.html

http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html

#include <setjmp.h>
#include <stdio.h>

jmp_buf x;

void f()
{
    longjmp(x,5); // throw 5;
}

int main()
{
    // output of this program is 5.

    int i = 0;

    if ( (i = setjmp(x)) == 0 )// try{
    {
        f();
    } // } --> end of try{
    else // catch(i){
    {
        switch( i )
        {
        case  1:
        case  2:
        default: fprintf( stdout, "error code = %d\n", i); break;
        }
    } // } --> end of catch(i){
    return 0;
}

#include <stdio.h>
#include <setjmp.h>

#define TRY do{ jmp_buf ex_buf__; if( !setjmp(ex_buf__) ){
#define CATCH } else {
#define ETRY } }while(0)
#define THROW longjmp(ex_buf__, 1)

int
main(int argc, char** argv)
{
   TRY
   {
      printf("In Try Statement\n");
      THROW;
      printf("I do not appear\n");
   }
   CATCH
   {
      printf("Got Exception!\n");
   }
   ETRY;

   return 0;
}

7voto

Alnitak Points 143355

Personnellement, je préfère la première approche (de retour d'un indicateur d'erreur).

Si nécessaire, le résultat de retour doit simplement indiquer qu'une erreur s'est produite, avec une autre fonction qui est utilisée pour trouver l'erreur exacte.

Dans votre getSize() exemple je considère que les tailles doivent toujours être nulle ou positive, afin de retourner un résultat négatif peut indiquer une erreur, comme beaucoup d'appels système UNIX ne.

Je ne peux pas penser à une bibliothèque que j'ai utilisé qui va pour la dernière approche avec une erreur de l'objet passé en tant que pointeur. stdio, etc vont tous avec une valeur de retour.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X