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:
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:
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:
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:
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.