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 :

construction de messages d'erreur formatés, une application des macros variadiques


Sujet :

C

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 38
    Points : 39
    Points
    39
    Par défaut construction de messages d'erreur formatés, une application des macros variadiques
    Bonjour,

    je souhaite définir une routine de débogage errorf (erreur formatée) qui puisse s'utiliser de la manière suivante (en C afin qu'elle puisse servir au moins en C et en C++) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    printf(errorf("fopen(\"%s\", \"%s\") failed", filename, mode)); // ligne 15 du fichier toto.c
     
    toto.c: 15: fread("titi", "r") failed
    Il faut donc une fonction variadique (i.e. à nombre variable d'arguments, comme printf), acceptant une chaîne de format avec la syntaxe de printf, et préfixant le message de l'appelant par le nom de fichier source et la ligne de code source où la fonction a été appelée.

    L'implémentation peut rester simple, un simple tampon de taille fixe alloué statiquement suffit pour ce besoin (les problèmes d'allocation dynamique, de garantie de réentrance etc, sont intéressants, mais ne sont pas le sujet de ce topic)

    D'abord, quelqu'un sait-il si une implémentation portable de cela (ou quelque chose d'approchant) existe ?

    Actuellement, j'ai écrit une implémentation d'errorf que voici. Elle n'est pas sans défauts.

    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
     
    /* errorf.h */
     
    /* Corentor, 2006 */
     
    #define errorf(message, ...) errorf_(__FILE__, __LINE__, (message), ##__VA_ARGS__)
     
    #ifdef __cplusplus
    extern "C" {
    #endif
     
    char *errorf_(const char *file_, int line_, const char *format, ...);
     
    #ifdef __cplusplus
    }
    #endif
    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
     
    /* errorf.c */
     
    /* Corentor, 2006 */
     
    #ifndef ERRORF_BUFSIZ
    #define ERRORF_BUFSIZ 65536
    #endif
     
    #include <assert.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdarg.h>
    #include "errorf.h"
     
    char *errorf_(const char *file_, int line_, const char *format, ...) {
      static char message[ERRORF_BUFSIZ];
      va_list arg;
      size_t header_len;
     
      va_start(arg, format);
      snprintf(message, sizeof(message), "%s: %d: ", file_, line_);
      header_len = strlen(message);
      assert(header_len < sizeof(message));
      vsnprintf(message + header_len, sizeof(message), format, arg);
      message[sizeof(message) - 1] = '\0';
      va_end(arg);
      return message;
    }
    Personne n'est infaillible (moi en particulier, pour des codes aussi simples), si un bogue vous saute aux yeux, merci de m'en faire part, ce sera très apprécié (à condition de rester courtois). Mais l'essentiel n'est pas là.

    Le buffer est alloué statiquement et est de taille fixe (risques de débordement), cela peut être amélioré, je me suis déjà exprimé là-dessus.

    Un oeil exercé reconnaîtra immédiatement que cette solution n'est pas portable.

    D'abord, les fonctions snprintf, vsnprintf, etc sont plus sûres que sprintf, vsprintf, etc mais sont évidemment moins portables. Je considère actuellement ce point comme pratiquement insignifiant, nous sommes en 2006 tout de même.

    Mais surtout, l'implémentation fait intervenir une macro variadique. A ma connaissance, ces macros n'ont été standardisées que depuis C99, bien qu'elles aient été supportées par certaines implémentations avant la définition du standard, sous forme d'extensions à la norme C89 (en particulier gcc).

    Actuellement, je ne vois pas de moyen de me passer d'une macro variadique pour ce type d'application (je dois accéder aux valeurs courantes de __FILE__ et __LINE__).

    Ce type de macro n'étant pas très souvent utilisé, je renvoie le lecteur à la documentation des macros variadiques dans gcc sur le site de gnu.

    Que les macros variadiques ne soient pas supportées avant C99 ne me gênent pas personnellement (au pire, je peux faire tester par le préprocesseur la version du standard supporté et réagir en conséquence).

    Ce qui m'ennuie bien plus, c'est que le code précédent n'est MEME PAS CONFORME AU STANDARD C99. En effet, j'ai été forcé d'utiliser les ## (non standard, extension gcc; attention il ne s'agit pas de l'opérateur ## classique du préprocesseur dans ce contexte) devant __VA_ARGS__ (standard C99) afin de permettre à l'appelant d'écrire des choses comme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    errorf("message d'erreur sans variable supplémentaire")
    Pour comprendre pourquoi cela est nécessaire, retourner au lien ci-dessus.

    Si quelqu'un a une idée de solution (je répète, portable, au moins au sens de la norme C99), je suis preneur. Les macros variadiques ne sont pas obligatoires : si on peut s'en passer, tant mieux.

    Ce topic peut être l'occasion de parler des macros variadiques en général (leur support dans les différentes implémentations etc), mais le sujet est et reste l'implémentation portable d'une fonction errorf telle que définie plus haut.

    Merci de votre attention.

  2. #2
    Membre éprouvé

    Profil pro
    Inscrit en
    Août 2003
    Messages
    878
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2003
    Messages : 878
    Points : 1 067
    Points
    1 067
    Par défaut
    Bonjour,

    Citation Envoyé par Corentor
    Les macros variadiques ne sont pas obligatoires : si on peut s'en passer, tant mieux.
    Sans avoir réfléchi à la question, je vois deux façons de se passer de macros variadiques [1] :
    - ne jamais appeler errorf() sans argument supplémentaire (toujours au moins un argument après la chaîne de format) ;
    - au lieu d'une fonction "errorf()", créer deux fonctions "errorPrint()" et "errorPrintf()".

    Dans le premier cas, même si c'est contraignant (toutjours trouver un argument à afficher) et oblige à une certaine discipline (ne pas oublier qu'il faut afficher un argument supplémentaire), je ne pens pas que cela soit extrêmement dificile. Par exemple : au lieu de ' errorf("échec de malloc()") ', on peut très bien (se forcer à) écrire ' errorf("échec de malloc(%i)", taille demandée) '.

    Dans le second, cela correspond un peu à la différence entre "puts()" et "printf()". Après tout, pourquoi utiliser une fonction d'affichage formaté quand on a pas besoin de formatage ?

    C'étaient mes deux centimes... (qui consistent à éviter le problème, c'est vrai)

    Cordialement,
    DS.

    [1] - En considérant que c'est bien ton but : j'ai peut-être mal compris.
    Un problème bien exposé
    est, pour moitié, solutionné. / La connaissance s'accroît quand on la partage, pas quand on l'impose. / La violence est le langage des faibles.

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 38
    Points : 39
    Points
    39
    Par défaut
    Citation Envoyé par David.Schris
    [1] - je vois deux façons de se passer de macros variadiques - En considérant que c'est bien ton but : j'ai peut-être mal compris
    Non, tu as bien compris. En fait, je ne cherche pas coûte que coûte à m'en débarrasser, mais à trouver une solution portable, et il ne semble pas que ce soit possible avec les macros variadiques de la norme actuelle.

    Pour ta première solution, j'y avais déjà pensé, on peut toujours s'y ramener en écrivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    errorf("%s", message_a_afficher)
    C'est juste un peu contraignant [1], c'est vrai que ce n'est pas un gros effort mais je voulais savoir s'il était possible de faire mieux (je pense utiliser cette fonction assez souvent dans mes codes de débogage). En soi cela ne me bloque pas. Dans ce cas, on peut se débarrasser des ## dans la définition de la macro et se ramener au standard C99.

    J'avais également envisagé la deuxième solution, mais je voulais éviter de dupliquer les fonctions (il est possible que je m'y ramène tout compte fait). C'est vrai qu'en l'absence de formatage puts est plus indiquée, mais force est de constater que même dans ce cas, une énorme majorité de gens continuent d'utiliser printf.

    Merci de ton intervention

    A bientôt

    [1] En fait, c'est surtout contraignant à documenter, si quelqu'un réutilise le code derrière. Il devra être informé qu'il faut toujours un argument obligatoire après la chaîne de format, contrairement à une utilisation intuitive à la printf (et comme personne ne lit les docs pour des fonctions aussi simples...)

  4. #4
    Membre éprouvé

    Profil pro
    Inscrit en
    Août 2003
    Messages
    878
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2003
    Messages : 878
    Points : 1 067
    Points
    1 067
    Par défaut
    Citation Envoyé par Corentor
    [1] En fait, c'est surtout contraignant à documenter, si quelqu'un réutilise le code derrière. Il devra être informé qu'il faut toujours un argument obligatoire après la chaîne de format, contrairement à une utilisation intuitive à la printf (et comme personne ne lit les docs pour des fonctions aussi simples...)
    Concernant le fait que "personne ne lit les docs pour des fonctions aussi simples", il y a peut-être un moyen d'améliorer un peu les choses : mettre l'information à un endroit et sous une forme qui limite le risque qu'ellene soit pas vue/lue. Par exemple, au lieu d'écrire des "docs pour des fonctions aussi simples", on peut intégrer les règles concernant ces fonctions en tête des autres docs dans un encart intitulé "IMPORTANT" (ou "OBLIGATOIRE", mais pas "Fonctions de logs") et écrit en rouge.
    Mais cela ne garantit rien.
    Sinon, on peut intégrer des vérifications concernant l'utilisation des fonctions de log dans les tests unitaires et rejeter tout code qui les utiliserait mal. C'est un peu brutal mais..."chat échaudé craint l'eau froide".

    Cordialement,
    DS.
    Un problème bien exposé
    est, pour moitié, solutionné. / La connaissance s'accroît quand on la partage, pas quand on l'impose. / La violence est le langage des faibles.

Discussions similaires

  1. mettre un message d'erreur dans une table
    Par sam01 dans le forum SQL Procédural
    Réponses: 1
    Dernier message: 16/12/2006, 16h56
  2. récupération des message d'erreurs dans une jsp
    Par clement42 dans le forum Struts 1
    Réponses: 17
    Dernier message: 23/10/2006, 11h59
  3. Afficher un message d'erreur dans une fenêtre popup
    Par Quickeno dans le forum Langage
    Réponses: 7
    Dernier message: 08/10/2006, 01h56
  4. Réponses: 2
    Dernier message: 02/08/2006, 19h55
  5. Réponses: 7
    Dernier message: 01/06/2005, 11h48

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