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++) :
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.
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
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 } #endifPersonne 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à.
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; }
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 :
Pour comprendre pourquoi cela est nécessaire, retourner au lien ci-dessus.
Code : Sélectionner tout - Visualiser dans une fenêtre à part errorf("message d'erreur sans variable supplémentaire")
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.
Partager