OVH Cloud OVH Cloud

OutputDebugString style "sprintf"

46 réponses
Avatar
stat
Bonjour,

J'ai besoin d'aide pour creer une fonction de type OutputDebugString
qui envoit un message de debuggage mais qui soit aussi facile a
utiliser que printf c'est a dire que je puisse l'utiliser de la sorte

OutputDebugStringExt("Valeur x: %i, valeur y: %i, Nom: %s\n", x, y,
Nom);

et j'avoue ne pas savoir du tout comment m'y prendre

Avez-vous une idee pour commencer svp ?

Merci.

--
This is an automatic signature of MesNews.
Site : http://www.mesnews.net

6 réponses

1 2 3 4 5
Avatar
Loïc Joly
Loïc Joly wrote on 25/11/2006 09:37:


J'ai du mal à te suivre : Tu prones une sois disant simplicité, ...



2 points ont été mélangés via ton post:
- le PO demandait comment traiter des pamarètres variables (réponse
va_list) et comment les mettre en forme (réponse vsprintf)
- tu as critiqué cette façon de faire en y opposant le dump d'un object.


Le PO demandait une méthode pour améliorer les possibilités de formatage
quand on envoie des données vers la console de débug. Il proposais une
syntaxe alternative, je lui en ai proposé une autre.


j'ai déjà largement exposé ici, notamment dans des échanges avec James,
que je sépare généralement la mise en forme d'un objet (sa
représentation, pouvant varier selon le besoin, d'où le switch) du
log/stockage/envoi de cette information formattée.


Moi aussi : L'opérateur << ne se charge que de la mise en forme, le fait
que je l'applique à un cout, file, log, autre... se charge de l'endroit
d'envoi. C'est le principe de base du découpage des flux en xstream et
xstreambuf. C'est d'ailleur un de mes reproches à printf qu'il ne gère
pas cette séparation.


on est d'accord - j'espère - sur le fait que la classe devra soit:
- fournir un operateur d'injection vers ostream (ton modèle)
- fournir une methode toString() (mon exemple)
le contenu de ces 2 fonctions/méthodes sera identique; il n'y a donc pas
critères déterminants de choix à ce niveau.


Je suis d'accord dans les grandes lignes. Dans les détails, je trouve
l'approche d'injection plus flexible, car l'utilisateur final peut à
l'aide des manipulateurs décider de certains paramètres d'affichage de
l'objet, sans en modifier le code, et éventuellement de façon différente
selon le flux (exemple : Je peux logger les doubles avec une bonne
précision dans un fichier, et de façon plus compacte pour l'écran).
J'avoue que je ne m'en sert pas tous les jours, mais quand j'ai envie de
m'en servir, j'aime bien que ça existe. En l'absence d'autres critères
de choix, je prends celui-ci.

Après, il y a le fait que je trouve plus direct d'injecter dans un flux
que de générer un string, le second ayant besoin dans beaucoup de cas de
passer par le premier (ce qui peut avoir un impact perfs, qui peut jouer
pour du log, mais là n'est pas mon propos). Mais il s'agit ici d'un
point de vue plus subjectif que d'un critère technique.


[...]

- logger l'information, ici prétendre "écrire dans un flux" ne répond
pas du tout: si "l'écriture sur le flux" est un log système (type
OutputDebugString) tu ne le réalises pas, si c'est une écriture en base
(le dump est une clause INSERT par exemple) tu ne leréalises pas plus.

je serais donc d'accord avec toi pour dire que l'écriture:
log << "Iteration " << i << " : Object = " << object;
apparait simple, l'honnêteté voudra que l'on précise que cela peut être
suffisant si et seulement si "log" est un "std::cout" (avec un marqueur
de fin d'injection cela pourrait être plus).


Je ne suis pas d'accord. Dans mon cas, log n'était absolument pas cout,
mais une instance d'une classe dérivée de ostream et écrite de telle
façon qu'elle utilise OutputDebugString pour ses sorties. Ou mieux (=
plus luxueux) encore qu'elle soit configurable pour logger vers
différentes destinations en fonction de la configuration de
l'utilisateur final de l'outil.

Pour l'histoire de marqueur de fin d'injection, si j'ai bien compris ce
que tu veux dire, un saut de ligne me suffit généralement pour les
aspects mise en forme du flux, et pour les aspects bufferisation, a
priori, un objet servant à du log, j'éviterai de lui adjoindre un buffer
plus long qu'une ligne. Si je n'ai pas compris, n'hésite pas à
développer ta pensée.


--
Loïc


Avatar
Sylvain
Jean-Marc Bourguet wrote on 27/11/2006 11:13:

Indefini ne veut d'ailleurs pas dire aleatoire ni non deterministe. Ca
veut dire que la norme n'impose rien aux implementations; et en pratique ca
veut dire que les implementations ne le definissent souvent pas et se
permettent de changer de comportement d'une version a l'autre ou en
fonctions de facteurs qu'elles ne prennent pas la peine d'annoncer.


nous sommes d'accord sur cette définition.

Le code que j'ecris est delivre avec 3 compilateurs differents (c'etait 4
il n'y a pas si longtemps). De ces 3 compilateurs, 2 donnent un
avertissement et un genere du code qui plante. Ce n'est pas un exercice
scolaire ni une speculation futuriste.


"scolaire" n'était pas péjoratif, je pensais, via cette expression, à
une exigence posée sur une portabilité (absolue et garantie), comme ce à
quoi peut/doit/? réfléchir un étudiant qui n'est pas marié avec un
compilo donné.

à l'opposé du cadre ""libre de contrainte"" de ce milieu scolaire,
l'industrie a (peut avoir) des containgances assez figées (j'utilise le
même compilo depuis plsu de 6 ans (si James, je le connais bien, par
contre je n'ai jamais dit "connaitre *les* compilateurs" comme une
science en soi). dans "mon" industrie wintel, seule la compatibilité
binaire compte et jamais ne se pose le souci de la recompilation du code
chez le client qui aura lui-même compilé son compilateur le matin même.
ceci autorise des codages "en fonction de" (y compris des lacunes et
manque de rigeur du dit compilo -- il peut être facile de prendre une
mauvaise habitude si le compilo n'a jamais indiqué le risque potentiel).

Mais je suis en effet tout a fait hors de la logique qui consiste a code en
fonction de ce que le compilateur que j'ai sous la main a l'air de faire.
Utiliser cette logique serait dans mon cas tout a fait irresponsable.


je ne dis pas non plus (et conseille encore moins) qu'il faille coder
n'importe comment du moment que cela semble passer.

Je cite donc une référence qui me semble appuyer mon propos en
précisant qu'il est possible que j'ai manqué quelque chose qui complète
la référence en introduisant un cas particulier -- les exemples dans la
norme de ce genre de choses sont légions. Je m'attendais à ce que tu
cites quelque chose appuyant ton point de vue (pas nécessairement la
norme -- pour moi c'est la référence où c'est le plus aisé de trouvé
quelque chose et la plus facile à citer parce qu'elle est sous forme de
pdf) mais tu me traites d'ignare, à moins que tu ne considères que je
t'ai traité d'ignare?
option 2 (non prise à titre personnel).



J'ai du mal a suivre ton raisonnement...


je ne peux rien te "citer" qui soit "de référence", ou alors seulement
indiquer l'absence d'interdiction et/ou mise en garde de telle écriture
par les compilos VC, cela n'avance pas.

pour reprendre le code listé, jamais je n'ai utilisé une telle écriture,
j'ai qlq fonctions variadiques dans ma toolbox ASN/PKCS mais toutes
reçoivent des pointeurs (pas des valeurs).
pour autant l'écriture (pour "indéfinie") ne me paraissait pas
non-compilable, on empile des variables (soit instance de classe) sur la
pile, on jumpe vers une fonction qui joue avec ces offsets sur la pile,
ceci n'avait presque rien à voir avec le C++, on aurait pu coder ça en
asm pour le même résultat; VC6 ne se soucie pas même du "&" dans la
déclaration, VC8 est lui gêné par une réf. mais dépile sans souci les
valeurs.

la question ne portait pas sur l'intérêt de passer une instance à une
fct var., quel (méta-)modèle original et fructueux cela aurait permis, à
ces questions il n'y aurait qu'une seule réponse: aucun.

Mais le compilateur qui gene est celui que nous utilisons pour une des
plateformes sur lesquelles nous delivrons, que tu le veuilles ou non.


bien sur que je le veux bien, aucun soucis même :)

[...] Par ailleurs, le compilateur sur lequel le code a l'air
de faire ce que tu veux [] sans donne un seul avertissement
est au contraire renomme pour sa rigueur.


je pense que - parlant de VC - tu voulais dire manque de rigueur
(l'anti-phrase est tjrs ambigu sur usenet); nous sommes d'accord sur ce
point également.

Sylvain.



Avatar
Sylvain
Jean-Marc Desperrier wrote on 27/11/2006 19:58:
Sylvain wrote:
static char message[2048];
_vsnprintf(message, 2048, pattern, params);
[...]
OutputDebugString(message);


N'y a-t-il pas une astuce comme quoi sur certaines implémentations il
n'est pas garanti que vsnprintf va écrire le zéro terminal quand le
buffer déborde ?


non je ne pense pas que cela soit garanti en effet, si la sortie ne
tient pas dans le buffer (dans les N car. indiqués), la fonction
retourne -1 ayant écrit N caractères (les N premiers car. du message à
formatter).

si la longueur de sortie ne peut pas être maximisée avec certitude, on
initialisera le tableau à zéro (ou le dernier car. de l'array) et on
transmettra (N - 1) comme longueur utilisable.

Sylvain.


Avatar
Sylvain
Loïc Joly wrote on 27/11/2006 21:08:

Le PO demandait une méthode pour améliorer les possibilités de formatage
quand on envoie des données vers la console de débug. Il proposais une
syntaxe alternative, je lui en ai proposé une autre.


abondance de solutions ne peut pas nuire.

j'ai déjà largement exposé ici, notamment dans des échanges avec
James, que je sépare généralement la mise en forme d'un objet (sa
représentation, pouvant varier selon le besoin, d'où le switch) du
log/stockage/envoi de cette information formattée.


Moi aussi : L'opérateur << ne se charge que de la mise en forme, le fait
que je l'applique à un cout, file, log, autre... se charge de l'endroit
d'envoi. C'est le principe de base du découpage des flux en xstream et
xstreambuf. C'est d'ailleur un de mes reproches à printf qu'il ne gère
pas cette séparation.


j'ai peut être mal exprimé mon point!
pour un enregistrement d'info., un "vrai", je fais cette séparation et
un injecteur (quel qu'il soit) sera généralement utile.
ici, la vocation, la finalité même de OutputDebugString n'est pas
comparable (elle ne sérialise rien, elle ne sert ni stocker, ni vraiment
à formatter), c'est du log debug pur jus.

dès lors on a le choix, pour chaque info à tracer, entre:

option 1:
- définition d'une classe 'log' wrappant OutputDebugString (once)
- usage de log << ... << ...;
option 2:
- usage d'un collecteur classique (std::string ou autre)
- appel à OutputDebugString ayant récupéré un const char*
option 3:
- codage d'un OutputDebugStringEx, une fois pour toute

je préfère nettement la 3ième et répondais en considérant qu'un
injecteur était ici (inutilement) trop luxueux.

[...]

Je ne suis pas d'accord. Dans mon cas, log n'était absolument pas cout,
mais une instance d'une classe dérivée de ostream et écrite de telle
façon qu'elle utilise OutputDebugString pour ses sorties.


ce n'est pas une solution applicable par la nature le log système; plein
d'applis (release, commerciale, etc) balance des pages entières (qui sur
un PC normal atterrissent nul part), si tu appelles OutputDebugString
pour chaque << ton log sera vraisemblablement mixé.

Ou mieux () encore qu'elle soit configurable pour logger vers
différentes destinations en fonction de la configuration de
l'utilisateur final de l'outil.


l'utilisateur final de OutputDebugString c'est le développeur, lui seul.
(ce luxe serait gaché).

Pour l'histoire de marqueur de fin d'injection, si j'ai bien compris ce
que tu veux dire, un saut de ligne me suffit généralement pour les
aspects mise en forme du flux, et pour les aspects bufferisation, a
priori, un objet servant à du log, j'éviterai de lui adjoindre un buffer
plus long qu'une ligne. Si je n'ai pas compris, n'hésite pas à
développer ta pensée.


tu as bien compris; du fait que OutputDebugString soit une poubelle,
j'envisagais une bufférisation ligne indispensable, dès lors un marqueur
"flush" serait nécessaire.

Sylvain.


Avatar
James Kanze
Sylvain wrote:

[...]
je serais donc d'accord avec toi pour dire que l'écriture:
log << "Iteration " << i << " : Object = " << object;
apparait simple, l'honnêteté voudra que l'on précise que cela peut être
suffisant si et seulement si "log" est un "std::cout" (avec un marqueur
de fin d'injection cela pourrait être plus).


Au contraire. La plupart du temps, chez moi, log est un wrapper
d'un ostream, dont le streambuf envoie à un fichier, syslog ou
un email, selon la configuration et la séverité, en insérant les
en-têtes de ligne, avec l'heure, etc. (En fait, « log » est
prèsque toujours un macro, pour insérer automatiquement __FILE__
et __LINE__ aussi.) La durée de vie du wrapper assure le
verouillage en cas de multi-thread, et que l'utilisation se
termine avec un saut à la ligne et un flush(). Le wrapper est
également conçu de façon à ce que le formattage n'a pas lieu si
le log n'est pas actif, de façon à pouvoir laisser le code actif
dans les versions de production.

Mais c'est peut-être plus qu'en démandait le posteur initial:-).
(Note qu'un tel système ne se justifie que si on va pouvoir
exploiter le log par la suite. C-à-d pour des applications
internes, ou celles où les utilisateurs sont assez avertis pour
garder des logs, des core dumps, etc. Certainement pas, en
revanche, pour des jeux ou des applications grande publiques.)

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
stat
mais juste de tracer (par exemple) si l'on rentre ou non dans une fonction
qui ne peut pas être tracée sous debugger symbolique.


C'est exactement mon probleme. Je dois lier mon appli avec une lib
tierce et il m'est impossible de faire tourner l'appli en mode
debuggage et je galere a mort !

Avec cette nouvelle fonction je galere moins grace a toi !

--
This is an automatic signature of MesNews.
Site : http://www.mesnews.net

1 2 3 4 5