IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

Les différentes façons de gérer les erreurs en C


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Chroniqueur Actualités
    Avatar de Bruno
    Homme Profil pro
    Rédacteur technique
    Inscrit en
    Mai 2019
    Messages
    2 117
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Cameroun

    Informations professionnelles :
    Activité : Rédacteur technique
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Mai 2019
    Messages : 2 117
    Par défaut Les différentes façons de gérer les erreurs en C
    Les différentes façons de gérer les erreurs en C,
    le C ne dispose pas d'une seule façon claire pour gérer les erreurs

    Le langage de programmation C offre au développeur une marge de contrôle importante sur la machine (notamment sur la gestion de la mémoire) et est de ce fait utilisé pour réaliser les « fondations » (compilateurs, interpréteurs…) des langages plus modernes. C'est un langage de programmation impératif généraliste, de bas niveau. Inventé au début des années 1970 pour réécrire Unix, C est devenu un des langages les plus utilisés, encore de nos jours. De nombreux langages plus modernes comme C++, C#, Java et PHP ou JavaScript ont repris une syntaxe similaire au C et reprennent en partie sa logique.

    En tant que tel, le langage C ne fournit pas de support direct pour la gestion des erreurs, mais étant un langage de programmation système, il vous fournit un accès à un niveau inférieur sous la forme de valeurs de retour. La plupart des appels de fonctions C ou même Unix renvoient -1 ou NULL en cas d'erreur et définissent un code d'erreur errno. Ainsi, un programmeur C peut vérifier les valeurs retournées et peut prendre les mesures appropriées en fonction de la valeur de retour.

    Nom : cB.png
Affichages : 816304
Taille : 43,7 Ko

    Essayons de simuler une condition d'erreur et d'ouvrir un fichier qui n'existe pas. Ici, les deux fonctions sont utilisées pour montrer leur usage, mais il est possible d'utiliser une ou plusieurs façons d'imprimer les erreurs. Le deuxième point important à noter est qu'il est possible d'utiliser le flux de fichiers stderr pour afficher toutes les erreurs.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
     
    extern int errno ;
     
    int main () {
     
       FILE * pf;
       int errnum;
       pf = fopen ("unexist.txt", "rb");
     
       if (pf == NULL) {
     
          errnum = errno;
          fprintf(stderr, "Value of errno: %d\n", errno);
          perror("Error printed by perror");
          fprintf(stderr, "Error opening file: %s\n", strerror( errnum ));
       } else {
     
          fclose (pf);
       }
     
       return 0;
    }

    Lorsque le code ci-dessus est compilé et exécuté, il produit le résultat suivant

    Value of errno: 2
    Error printed by perror: No such file or directory
    Error opening file: No such file or directory


    Le C n'a pas une seule façon claire de gérer les erreurs

    Statut de sortie

    Il fournit une fonction exit() qui prend deux valeurs pour afficher une fin réussie ou non réussie en utilisant EXIT_SUCCESS et EXIT_FAILURE. Cette fonction exit() est définie dans le fichier d'en-tête stdlib.h de la bibliothèque standard.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <stdlib.h>
    int main ()
    {
    FILE * f;
    f = fopen ("article.txt", "rb");
    if (f == NULL)
    {
    printf("The Value of errno printed is : %d\n", errno);
    printf("Error message printed while opening the file with errno: %s\n",
    strerror(errno));
    perror("Error message printed by perror");
    exit(EXIT_FAILURE);
    printf("The message will not be printed\n");
    }
    else
    {
    fclose (f);
    exit(EXIT_SUCCESS);
    printf("The message will be printed\n");
    }
    return 0;
    }

    L'algorithme de l'autruche

    Si une condition d'erreur est suffisamment rare, il est toujours possible de faire l'autruche et choisir d'ignorer cette possibilité. Cela peut rendre le code beaucoup plus joli, mais au détriment de la robustesse.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #include <stdio.h>
     
    int parse_natural_base_10_number(const char* s) {
        int parsed = 0;
        for (size_t i = 0; s[i] != '\0'; i++) {
            parsed *= 10;
            parsed += s[i] - '0';
        }
     
        return parsed;
    }
     
     
    int main() {
        printf("Expecting garbage or crash on bad values\n");
        const char* examples[] = { "10", "foo", "42", "" };
        for (size_t i = 0; i < 4; i++) {
            const char* example = examples[i];
            int parsed = parse_natural_base_10_number(example);
            printf("parsed: %d\n", parsed);
        }
     
        return 0;
    }

    Expecting garbage or crash on bad values
    parsed: 10
    parsed: 6093
    parsed: 42
    parsed: 0


    Un exemple concret de cela peut être observé avec l'utilisation de malloc par le micrologiciel des dispositifs de flipper.

    Crash

    Parfois, les erreurs sont pratiquement irrécupérables. Selon McCue, la plupart des applications devraient probablement abandonner lorsque malloc renvoie NULL. Si vous êtes sûr qu'il n'y a pas de moyen de récupérer une condition d'erreur et que l'appelant ne voudra pas la gérer d'une autre manière, il est simplement possible d'imprimer un message disant ce qui s'est mal passé et quitter le programme.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
    #include <stdio.h>
    #include <stdlib.h>
     
    int parse_natural_base_10_number(const char* s) {
        int parsed = 0;
        for (size_t i = 0; s[i] != '\0'; i++) {
            if (s[i] < '0' || s[i] > '9') {
                printf(
                   "Got a bad character ('%c') in %s, crashing.", 
                   s[i], 
                   s
                );
                exit(1);
            }
            else {
                parsed *= 10;
                parsed += s[i] - '0';
            }
        }
     
        return parsed;
    }
     
    int main() {
        const char* examples[] = { "10", "42", "foo" };
        for (size_t i = 0; i < 3; i++) {
            const char* example = examples[i];
            int parsed = parse_natural_base_10_number(example);
            printf("parsed: %d\n", parsed);
        }
     
        return 0;
    }

    parsed: 10
    parsed: 42
    Got a bad character ('f') in foo, crashing.


    Retourner un nombre négatif

    Si la fonction renvoie normalement un nombre naturel, il est possible d'utiliser un nombre négatif pour indiquer un échec. Cela s'applique aussi bien à l'exemple qu'à des cas tels que le renvoi du nombre d'octets lus dans un fichier. S'il existe différents types d'erreurs pour ce genre de cas,il est également possible d'utiliser des nombres négatifs spécifiques pour indiquer les différentes catégories.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    #include <stdio.h>
     
    int parse_natural_base_10_number(const char* s) {
        int parsed = 0;
        for (size_t i = 0; s[i] != '\0'; i++) {
            if (s[i] < '0' || s[i] > '9') {
                return -1;
            }
            else {
                parsed *= 10;
                parsed += s[i] - '0';
            }
        }
     
        return parsed;
    }
     
    int main() {
        const char* examples[] = { "10", "foo", "42" };
        for (size_t i = 0; i < 3; i++) {
            const char* example = examples[i];
            int parsed = parse_natural_base_10_number(example);
            if (parsed < 0) {
                printf("failed: %s\n", example);
            }
            else {
                printf("worked: %d\n", parsed);
            }
        }
     
        return 0;
    }

    worked: 10
    failed: foo
    worked: 42


    Retourner NULL

    Si la fonction renvoie normalement un pointeur, il est possible d'utiliser NULL pour indiquer que quelque chose s'est mal passé. La plupart des fonctions qui renverraient des pointeurs effectueraient une allocation au tas pour que cela soit sain, donc ce schéma n'est probablement pas applicable lorsque vous voulez éviter les allocations. Pour McCue, il serait stupide d'allouer un [/C]int[/C] au tas.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    #include <stdio.h>
    #include <stdlib.h>
     
    int* parse_natural_base_10_number(const char* s) {
        int parsed = 0;
        for (size_t i = 0; s[i] != '\0'; i++) {
            if (s[i] < '0' || s[i] > '9') {
                return NULL;
            }
            else {
                parsed *= 10;
                parsed += s[i] - '0';
            }
        }
     
        int* result = malloc(sizeof (int));
        *result = parsed;
        return result;
    }
     
    int main() {
        const char* examples[] = { "10", "foo", "42" };
        for (size_t i = 0; i < 3; i++) {
            const char* example = examples[i];
            int* parsed = parse_natural_base_10_number(example);
            if (parsed == NULL) {
                printf("failed: %s\n", example);
            }
            else {
                printf("worked: %d\n", *parsed);
            }
     
            free(parsed);
        }
     
        return 0;
    }

    worked: 10
    failed: foo
    worked: 42


    Un exemple concret de ce schéma est celui de malloc. Si malloc ne parvient pas à allouer de la mémoire, au lieu de renvoyer un pointeur vers la mémoire nouvellement allouée, il renvoie un pointeur nul.

    Retourner un booléen et prendre un paramètre externe

    L'une des choses les moins évidentes qu'il est possible de faire en C est d'avoir un ou plusieurs arguments d'une fonction out params. Cela signifie qu'il fait partie du contrat de la fonction qu'elle écrira dans la mémoire derrière un pointeur. Si une fonction peut échouer, une traduction naturelle de ceci peut être de retourner un booléen indiquant si elle l'a fait et de passer un paramètre out qui n'est n'inspecté que lorsque true est retourné.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    #include <stdio.h>
    #include <stdbool.h>
     
    bool parse_natural_base_10_number(const char* s, int* out) {
        int parsed = 0;
        for (size_t i = 0; s[i] != '\0'; i++) {
            if (s[i] < '0' || s[i] > '9') {
                return false;
            }
            else {
                parsed *= 10;
                parsed += s[i] - '0';
            }
        }
     
        *out = parsed;
        return true;
    }
     
    int main() {
        const char* examples[] = { "10", "foo", "42" };
        for (size_t i = 0; i < 3; i++) {
            const char* example = examples[i];
            int parsed;
            bool success = parse_natural_base_10_number(
                example, 
                &parsed
            );
            if (!success) {
                printf("failed: %s\n", example);
            }
            else {
                printf("worked: %d\n", parsed);
            }
        }
     
        return 0;
    }

    Retourner une énumération et prendre un paramètre de sortie

    Un booléen peut seulement indiquer que quelque chose a réussi ou échoué. Si vous voulez savoir pourquoi quelque chose a échoué, remplacer un enum par un booléen est un mécanisme assez naturel.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    #include <stdio.h>
     
    enum ParseNaturalNumberResult {
        PARSE_NATURAL_SUCCESS,
        PARSE_NATURAL_EMPTY_STRING,
        PARSE_NATURAL_BAD_CHARACTER
    } ;
     
    enum ParseNaturalNumberResult parse_natural_base_10_number(
       const char* s, 
       int* out
    ) {
        if (s[0] == '\0') {
            return PARSE_NATURAL_EMPTY_STRING ;
        }
     
        int parsed = 0 ;
        for (size_t i = 0 ; s[i] != '\0' ; i++) {
            if (s[i] < '0' || s[i] > '9') {
                return PARSE_NATURAL_BAD_CHARACTER ;
            }
            else {
                parsed *= 10 ;
                parsed += s[i] - '0' ;
            }
        }
     
        *out = parsed ;
        return PARSE_NATURAL_SUCCESS ;
    }
     
    int main() {
        const char* examples[] = { "10", "foo", "42", "" } ;
        for (size_t i = 0 ; i < 4 ; i++) {
            const char* exemple = exemples[i] ;
            int parsed ;
            switch (parse_natural_base_10_number(example, &parsed)) {
                cas PARSE_NATURAL_SUCCESS :
                    printf("a fonctionné : %d\n", parsed) ;
                    pause ;
                cas PARSE_NATURAL_EMPTY_STRING :
                    printf("failed because empty string\n") ;
                    pause ;
                cas PARSE_NATURAL_BAD_CHARACTER :
                    printf("failed because bad char : %s\n", exemple) ;
                    break ;
            }
        }
     
        return 0 ;
    }
    worked: 10
    failed because bad char: foo
    worked: 42
    failed because empty string


    Retourner un booléen et prendre deux paramètres en sortie

    Alors qu'un enum peut donner la "catégorie" d'une erreur, il n'a pas de place pour enregistrer des informations plus spécifiques que cela. Par exemple, il est assez raisonnable de vouloir savoir, si vous rencontrez un caractère inattendu, où se trouve ce caractère dans la chaîne de caractères. En ajoutant un deuxième paramètre out, il est possible d'avoir un endroit pour mettre cette information.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    #include <stdio.h>
    #include <stdbool.h>
     
    bool parse_natural_base_10_number(
       const char* s, 
       int* out_value, 
       size_t* out_bad_index
    ) {
        int parsed = 0;
        for (size_t i = 0; s[i] != '\0'; i++) {
            if (s[i] < '0' || s[i] > '9') {
                *out_bad_index = i;
                return false;
            }
            else {
                parsed *= 10;
                parsed += s[i] - '0';
            }
        }
     
        *out_value = parsed;
        return true;
    }
     
    int main() {
        const char* examples[] = { "10", "foo", "42", "12a34" };
        for (size_t i = 0; i < 4; i++) {
            const char* example = examples[i];
     
            int parsed;
            size_t bad_index;
            bool success = parse_natural_base_10_number(
                example, 
                &parsed, 
                &bad_index
            );
            if (!success) {
                printf("failed: %s\n        ", example);
                for (size_t j = 0; j < bad_index; j++) {
                    printf(" ");
                }
                printf("\n");
            }
            else {
                printf("worked: %d\n", parsed);
            }
        }
     
        return 0;
    }

    worked: 10
    failed: foo

    worked: 42
    failed: 12a34


    Retourner un enum et plusieurs paramètres de sortie

    Une extension naturelle des deux modèles précédents est que si vous avez plusieurs façons dont un calcul peut échouer, il est possible de retourner un enumavec chaque façon et prendre un paramètre out pour chaque façon qui nécessiterait des données.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    #include <stdio.h>
    #include <string.h>
     
    enum ParseNaturalNumberResult {
        PARSE_NATURAL_SUCCESS,
        PARSE_NATURAL_EMPTY_STRING,
        PARSE_NATURAL_BAD_CHARACTER,
        PARSE_NUMBER_TOO_BIG
    };
     
    struct BadCharacterInfo {
        size_t index;
    };
     
    struct TooBigInfo {
        size_t remaining_characters;
    };
     
    enum ParseNaturalNumberResult parse_natural_base_10_number(
            const char* s,
            int* out_value,
            struct BadCharacterInfo* bad_character_info,
            struct TooBigInfo* too_big_info
    ) {
        if (s[0] == '\0') {
            return PARSE_NATURAL_EMPTY_STRING;
        }
     
        int parsed = 0;
        for (size_t i = 0; s[i] != '\0'; i++) {
            if (s[i] < '0' || s[i] > '9') {
                bad_character_info->index = i;
                return PARSE_NATURAL_BAD_CHARACTER;
            }
            else {
                int digit = s[i] - '0';
                int new_parsed = (parsed * 10) + digit;
                if ((new_parsed - digit) / 10 != parsed) {
                    too_big_info->remaining_characters = strlen(s) - i;
                    return PARSE_NUMBER_TOO_BIG;
                }
                else {
                    parsed = new_parsed;
                }
            }
        }
     
        *out_value = parsed;
        return PARSE_NATURAL_SUCCESS;
    }
     
    int main() {
        const char* examples[] = { "10", 
                                   "foo", 
                                   "42", 
                                   "", 
                                   "99999999999999" };
        for (size_t i = 0; i < 5; i++) {
            const char* example = examples[i];
            int parsed;
            struct BadCharacterInfo bad_character_info;
            struct TooBigInfo too_big_info;
     
            switch (parse_natural_base_10_number(
                example, 
                &parsed, 
                &bad_character_info,
                &too_big_info
            )) {
                case PARSE_NATURAL_SUCCESS:
                    printf("worked: %d\n", parsed);
                    break;
                case PARSE_NATURAL_EMPTY_STRING:
                    printf("failed because empty string\n");
                    break;
                case PARSE_NATURAL_BAD_CHARACTER:
                    printf(
                        "failed because bad char at index %zu: %s\n",
                        bad_character_info.index,
                        example
                    );
                    break;
                case PARSE_NUMBER_TOO_BIG:
                    printf(
                        "number was too big. had %zu digits left: %s\n",
                        too_big_info.remaining_characters,
                        example
                    );
                    break;
            }
        }
     
        return 0;
    }

    worked: 10
    failed because bad char at index 0: foo
    worked: 42
    failed because empty string
    number was too big. had 5 digits left: 99999999999999


    Définir une valeur statique locale de thread

    Une autre option consiste à définir, en cas d'erreur, une variable statique locale. Cela évite d'avoir à propager explicitement une erreur tout le long de la pile à partir de l'endroit où elle se produit et rend l'API "normale" de la fonction aussi propre et nette que les approches autruche ou crash. Une fois que vous avez défini la valeur statique locale du thread, vous pouvez soit :

    1. Retourner une valeur prévisible indiquant un problème (NULL, un nombre négatif, etc.), ce qui incite le programmeur à vérifier la valeur statique locale du thread.
    2. Retourner une valeur non initialisée et compter sur le programmeur pour savoir que la valeur pourrait être fausse à moins qu'il ne vérifie la valeur statique locale du thread.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    #include <stdio.h>
    #include <stdbool.h>
     
    _Thread_local static bool parse_number_error = false;
     
    int parse_natural_base_10_number(const char* s) {
        int parsed = 0;
        for (size_t i = 0; s[i] != '\0'; i++) {
            if (s[i] < '0' || s[i] > '9') {
                parse_number_error = true;
            }
            else {
                parsed *= 10;
                parsed += s[i] - '0';
            }
        }
     
        return parsed;
    }
     
    int main() {
        const char* examples[] = { "10", "42", "foo" };
        for (size_t i = 0; i < 3; i++) {
            const char* example = examples[i];
            int parsed = parse_natural_base_10_number(example);
            if (parse_number_error) {
                parse_number_error = false;
                printf("error: %s\n", example);
            }
            else {
                printf("parsed: %d\n", parsed);
            }
        }
     
        return 0;
    }

    parsed: 10
    parsed: 42
    error: foo


    Un grand nombre d'apis intégrées utilisent une constante statique partagée appelée errno et si elles échouent, elles lui attribuent une valeur non nulle. Il existe ensuite des fonctions comme perror qui peuvent extraire des messages à partir du code d'erreur spécifique. Techniquement, il est possible utiliser errno aussi, tant que vos conditions d'erreur peuvent tenir dans son encodage int.

    Des langages tels C2 ou C3 pour remplacer le C ?

    Comme dit précédemment, la gestion des erreurs dans le langage de programmation C n'est pas prise en charge, car il fournit quelques fonctions et des valeurs de numéros d'erreur qui sont imprimées comme des messages d'erreur. Le lamgage ne dispose que d'un support de bibliothèque très limité : il faut ajouter des chemins de recherche pour les fichiers d'en-tête, inclure certains fichiers d'en-tête et établir des liens avec des bibliothèques statiques ou dynamiques. Ces étapes sont toutes séparées. Si vous appelez des fonctions de bibliothèque sans les lier, vous pouvez avoir des références non définies.

    C2 a corrigé ce problème en faisant de l'utilisation de la bibliothèque une chose totalement automatique. Vous utilisez la bibliothèque ou vous ne l'utilisez pas. De plus, C2 supporte les bibliothèques sources. Il s'agit de bibliothèques qui sont utilisées sous forme de source (=C2). Cela permet une meilleure intégration et optimisation, en particulier lors de l'utilisation de nombreuses fonctions "simples" qui ne font que renvoyer un membre d'une structure opaque, par exemple. Cela permet également aux développeurs d'organiser leurs archives de code d'une manière beaucoup plus facile.

    C3 est un langage de programmation système basé sur le C. C'est une évolution du C permettant les mêmes paradigmes et conservant la même syntaxe dans la mesure du possible. C3 a commencé comme une extension du langage C2 par Bas van den Berg. Il a évolué de manière significative, non seulement au niveau de la syntaxe mais aussi en ce qui concerne la gestion des erreurs, les macros, les génériques et les chaînes de caractères.

    Source : Mccue

    Et vous ?

    Que pensez-vous du langage C ? Dépassé ou d'actaulité

    Quel langage de programmation utilisez-vous pour contourner les manquements du C ?

    À votre avis, C2 et C3 pourraient remplacer le C ?

    N'aurait-il pas été plus bénéfique de conjuguer les efforts pour améliorer le C plutôt que de créer C2 ou encore C3 et peut être C4 ?

    Voir aussi :

    C3 : un langage de programmation système basé sur le C, permet un accès sécurisé aux tableaux, les conteneurs de haut niveau et manipulation des chaînes de caractères

    Microsoft célèbre les 20 ans de .NET, son Framework de développement, les dépôts .NET seraient dans le top 30 des projets open source à plus haute vélocité sur GitHub depuis 2017

    Microsoft a publié la version stable de Visual Studio 2022 avec une nouvelle expérience de rechargement à chaud pour les applications natives C++, cette version est disponible uniquement en 64 bits

    Un développeur publie un langage de programmation qui peut être traduit automatiquement en C, C++, C#, Java, JavaScript, etc., avec une traduction rapide et sans machine virtuelle
    Contribuez au club : corrections, suggestions, critiques, ... Contactez le service news et Rédigez des actualités

  2. #2
    Membre émérite
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Juillet 2020
    Messages
    352
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Juillet 2020
    Messages : 352
    Par défaut
    Bonjour,
    une «autre méthode» (classique) consiste à utiliser errno pour indiquer le statut du résultat. On assigne 0 à errno, on effectue l'opération, si une erreur survient alors errno n'est plus nul et contient un code erreur ; on pourra avantageusement réutiliser les codes standards. Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
        errno=0;
        int n=parse_natural_base_10_number(test_string);
        if (errno) {
            perror("parsing failed");
        } else {
            printf("parsed %d\n", n);
        }
    parse_natural_base_10_number pourrait par exemple utiliser EINVAL si la chaîne contient des caractères illégaux, ERANGE si la chaîne contient bien un nombre mais irreprésentable en int, etc.


    Sinon, dans le même genre, on peut créer un type adéquat. On ne parse pas une chaîne, on essaye de la parser et du coup le résultat d'une telle opération devrait être non un entier mais un type représentant soit un (succès, entier) soit un (échec, raison). Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
        enum try_parse_error { INVALID_CHAR, RANGE };
        const char *try_parse_error_str[] = {
            [INVALID_CHAR]="unexpected char",
            [RANGE]="integer range error",
        };
     
        struct try_parse_int_result {
            bool success;
            union {
                int value;
                enum try_parse_error error;
            }
        }
     
    ...
     
        struct try_parse_int_result result=try_parse_int(test_string);
        if (result.success) {
            printf("parsed %d\n", result.value);
        } else {
            printf("parsing failed with error : %s\n", try_parse_error_str[result.error]);
        }

  3. #3
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par WhiteCrow Voir le message
    Sinon, dans le même genre, on peut créer un type adéquat. On ne parse pas une chaîne, on essaye de la parser et du coup le résultat d'une telle opération devrait être non un entier mais un type représentant soit un (succès, entier) soit un (échec, raison).
    Solution très élégante Mais je préfère celle du errno qui me semble être justement le truc fait pour ça.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  4. #4
    Membre Expert
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    871
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2010
    Messages : 871
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Bonjour

    Solution très élégante Mais je préfère celle du errno qui me semble être justement le truc fait pour ça.
    Jusqu'au jour où tu fais du multi-threading. Les variables globales comme errno deviennent beaucoup moins fun d'un coup.

  5. #5
    Membre émérite
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Juillet 2020
    Messages
    352
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Juillet 2020
    Messages : 352
    Par défaut
    De nos jours errno est thread local … donc moins de soucis de côté là. En revanche il y a toujours un problème de réentrance dans un même thread, genre un signal handler qui se déclenche et modifie errno.
    Il faut clairement plus de rigueur quand on utilise errno que lors qu'on définit un type adapté, chose que l'on retrouve dans la plupart des langages plus récents.

  6. #6
    Membre émérite
    Homme Profil pro
    Architecte cybersécurité
    Inscrit en
    Avril 2014
    Messages
    566
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Architecte cybersécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2014
    Messages : 566
    Par défaut
    Ou la gestion des exceptions: setjmp() longjmp()

  7. #7
    Membre actif
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juillet 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Juillet 2012
    Messages : 25
    Par défaut
    Citation Envoyé par imperio Voir le message
    Jusqu'au jour où tu fais du multi-threading. Les variables globales comme errno deviennent beaucoup moins fun d'un coup.

    La variable errno est propre au thread, ce n'est pas une pas une variable globale.

    https://stackoverflow.com/questions/1694164/is-errno-thread-safe

  8. #8
    Membre chevronné
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    1 854
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 1 854
    Par défaut
    Et comment vous faite pour afficher les noms des fonctions qui ont appelées la fonction qui génère l'erreur ?... c'est très pratique pour le debug

  9. #9
    Membre très actif Avatar de vivid
    Profil pro
    Inscrit en
    Février 2006
    Messages
    212
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 212
    Par défaut
    A vouloir tout simplifier on ce retrouve forcément avec une perte de technicité.., exemple ; la daube JAVA

Discussions similaires

  1. Réponses: 5
    Dernier message: 06/11/2020, 08h45
  2. Différentes façons de gérer les erreurs
    Par Maniz dans le forum VB.NET
    Réponses: 2
    Dernier message: 27/10/2011, 11h46
  3. Proc. Stock. : Gérer les erreurs
    Par audreyc dans le forum SQL Procédural
    Réponses: 4
    Dernier message: 29/03/2006, 14h51
  4. gérer les erreurs intebase avec delphi
    Par mondanikoffi dans le forum Bases de données
    Réponses: 1
    Dernier message: 14/12/2004, 15h46

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo