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

10 réponses

1 2 3 4 5
Avatar
Sylvain
David wrote on 25/11/2006 00:55:

la problématique, d'accès concurrentiel notamment, sera (quasiment)
exactement la même pour coder le ostream& << (ostream&, Object&);
(quasiment car le problème de la chaine retournée n'existe que pour
l'appel à une fonction).


A 1h00 du matin et rapidement, je n'ai pas trouvé les mêmes problèmes
dans ces deux approches.

J'imaginais quelque chose comme ça pour toString() :
const char* object::toString() const {
...
return quelquechose;
}


oui, quelque chose cela, où "..." n'est pas
char* quelquechose = new char[aLength];
sprintf(quelquechose, "<format>", members);

ni
string quelquechose;
quelquechose << members;
(~quelquechose sera appelé avant le <<(const char*))

mais plutôt

class Object {
#ifdef _DEBUG
private:
String _dump;
int _level;
public:
const char* toString(){
_dump.empty();
switch (_level){
case ...
_dump << member1 << ...;
break;
...
}
return _dump;
}
#endif
};

et là je ne vois pas trop de solution simple notamment pour la
concurence sur un hypothétique buffer static (un peu à la "C" je dirais)


pour éviter la ""complexité"" d'une string interne (et/ou par
étourderie) on pourrait se contenter d'un buffer global static, ce
serait là une erreur en effet.

un tel buffer (unique) fournirait des contenus invalides en cas d'accès
concurrentiel à 'la' méthode toString par différentes instances; je
pensais également à des accès concurrentiels sur la même instance (le
même object dumpé par plusieurs threads en même temps), là toutes les
variantes listées ici échouent.

alors qu'avec un
std::string object::toString() const {
// ou asString(), j'hésite toujours.
string tmp;
// ...
return tmp;
}


c'est une option possible, elle évite une donnée membre String, et
limite la problématique des accès concurrentiels au seul temps du
remplissage de 'tmp'.

pour ma part, j'hésiterai un peu en faveur de la première façon car
"std::string" n'est pas (pour moi et même si pour moi seul) un type
standard, comme l'est un const char*; utiliser un string en interne à ma
méthode est ma toutouille et me va, forcer l'utilisateur externe à
connaitre ce type, ne me convient pas.


puis, pour faciliter une utilisation en mode flux :
ostream& operator << ( ostream& os, const object& o ) {
return os << o.toString();
}


ou alors, avec la première version:

class Object {
...
virtual const char* toString(){ ... }
operator const char* () const { return toString(); }
};

qui permet d'utiliser directement: "ostream << instance;" sans coder un
opérateur explicite.

Sylvain.


Avatar
Sylvain
David wrote on 25/11/2006 00:55:

J'imaginais quelque chose comme ça pour toString() :
const char* object::toString() const {
...
return quelquechose;
}


j'ai répondu sur cette hypothèse d'un retour "const char*" (que je peux
en effet parfois préférer).

ce n'était pas dans mes réponses précédentes; on peut partir, bien sur,
sur un retour std::string, qui, par recopie, donne le résultat attendu.

Sylvain.

Avatar
Loïc Joly

mais plutôt

class Object {
#ifdef _DEBUG
private:
String _dump;
int _level;
public:
const char* toString(){
_dump.empty();
switch (_level){
case ...
_dump << member1 << ...;
break;
...
}
return _dump;
}
#endif
};



J'ai du mal à te suivre : Tu prones une sois disant simplicité, mais
alors que dans mon exemple, on doit une seule fois surcharger 2
fonctions d'un streambuf, au lieu d'écrire 1 fonction à paramètres
variables (la complexité plus élevée est discutable), dans le tiens, à
chaque classe tu demandes à l'utilisateur d'écrire une fonction toString
et d'ajouter nue variable membre, cette fonction passant par des
ostringstreams, que tu as éludé en écrivant _dump << member1, avec _dump
une variable d'une classe String non définie, là où je réutilise le
mécanisme standard d'écrire dans un flux.

Donc si je t'ai suivi, tu écris dans un flux pour mettre dans une string
pour mettre dans un char*, et tu trouve ça plus simple que d'écrire
directement dans un flux ?

--
Loïc

Avatar
Loïc Joly
David wrote on 25/11/2006 00:55:


J'imaginais quelque chose comme ça pour toString() :
const char* object::toString() const {
...
return quelquechose; }



j'ai répondu sur cette hypothèse d'un retour "const char*" (que je peux
en effet parfois préférer).

ce n'était pas dans mes réponses précédentes; on peut partir, bien sur,
sur un retour std::string, qui, par recopie, donne le résultat attendu.


Et n'est pas utilisable directement dans un printf...

--
Loïc


Avatar
Loïc Joly
Loïc Joly wrote on 24/11/2006 23:14:

En général, ce que j'aime logger ressemble plus à :
"Itération 3 : Objet = {1, 3, "Toto", true}"
Et que j'aime écrire :
log << "Iteration " << i << " : Object = " << object;

Je ne sais pas écrire la même chose simplement avec printf.



*tu* ne sais pas, et alors ?
d'autre sauront écrire
printf("Iteration %d : Object = %s", i, object.toString());
(en essayant de donner un sens à "log << object" qui ne peut rien dire à
sa seule lecture (obfuscation ici non décriée)).


Envoyer une donnéee sur un flux, tu appelle ça de l'obfuscation ?! C'est
pourtant un des mécanismes de base du langage.

--
Loïc


Avatar
Sylvain
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.

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.

cette distinction faite, et si on veut argumenter sur le dump d'un
objet, la mise en forme du dump de celui-ci devra être faite quelque
soit la façon dont je l'envoie, le print, le log, ..., il n'y a de
simplicité obligeatoire et nécessaire ici, si l'objet (et/ou les besoins
d'informations sur cet objet) est compliqué, la génération de son dump
sera compliqué; par contre transmettre ce dump pourra être "aussi
simple" qu'un printf("%s", ...) si ce contexte de transmission n'a pas
besoin / n'est pas basé sur des iostreams.


... alors que dans mon exemple, on doit une seule fois surcharger 2
fonctions d'un streambuf, au lieu d'écrire 1 fonction à paramètres
variables (la complexité plus élevée est discutable), dans le tiens, à
chaque classe tu demandes à l'utilisateur d'écrire une fonction toString
et d'ajouter nue variable membre, cette fonction passant par des
ostringstreams, que tu as éludé en écrivant _dump << member1, avec _dump
une variable d'une classe String non définie, là où je réutilise le
mécanisme standard d'écrire dans un flux.


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.

factuellement:
- une variable membre est nécessaire que si le type retourné est const
char* (là j'ai suivi l'idée de David, je ne l'ai pas imposé a priori) et
variable (un log possible pour la classe 'Toto' pourrait être /return
"Toto";/ (chaîne C static que l'on utiliserait par exemple pour vérifier
que la bonne classe (d'un schéma d'héritage) est utilisée).
- je n'élude pas le dump de l'objet, je répète que c'est une opération
en soi (indépendante de sa transmission) et qu'elle est variable (de la
chaine constante ou formattage complet de tous ses membres, en passant
par le TLV, requete SQL et autre XML déjà évoqué pour le même sujet).
- la classe String est ma classe wrapper de chaine de caractères, je
n'utilise jamais std::string, j'ai laissé ce nom ici car il est
suffisant pour permettre à tout le monde de comprendre son role.

Donc si je t'ai suivi, tu écris dans un flux pour mettre dans une string
pour mettre dans un char*, et tu trouve ça plus simple que d'écrire
directement dans un flux ?


encore une fois, il y a (IMHO) ici une mélange des roles:
- 'fluxer' l'objet, j'ai besoin quelque soit le reste de faire cette
opération
- 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).

Sylvain.

Avatar
Sylvain
Loïc Joly wrote on 25/11/2006 09:38:
[] on peut partir, sur un retour std::string,
qui donne le résultat attendu.


Et n'est pas utilisable directement dans un printf...


?! parce qu'il n'a pas d'opérateurs de coercion vers const char* ?
une raison de plus (pour moi) de ne pas l'utiliser.

Sylvain.


Avatar
Michel Decima
Sylvain wrote:
Loïc Joly wrote on 25/11/2006 09:38:
[] on peut partir, sur un retour std::string, qui donne le résultat
attendu.


Et n'est pas utilisable directement dans un printf...


?! parce qu'il n'a pas d'opérateurs de coercion vers const char* ?
une raison de plus (pour moi) de ne pas l'utiliser.


Je ne vois pas vraiment le rapport entre un operateur de conversion
automatique vers const char* et la compatibilité avec printf.



Avatar
Jean-Marc Bourguet
Michel Decima writes:

Sylvain wrote:
Loïc Joly wrote on 25/11/2006 09:38:
[] on peut partir, sur un retour std::string, qui donne le résultat
attendu.


Et n'est pas utilisable directement dans un printf...
?! parce qu'il n'a pas d'opérateurs de coercion vers const char* ?

une raison de plus (pour moi) de ne pas l'utiliser.


Je ne vois pas vraiment le rapport entre un operateur de conversion
automatique vers const char* et la compatibilité avec printf.


Il n'y en a pas. Passer une instance de classe à une fonction variadique,
c'est un comportement indéfini, quelles que soient les conversions
implicites disponibles pour cette classe.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org




Avatar
Sylvain
Michel Decima wrote on 25/11/2006 20:35:
Sylvain wrote:
Loïc Joly wrote on 25/11/2006 09:38:
[] on peut partir, sur un retour std::string, qui donne le résultat
attendu.


Et n'est pas utilisable directement dans un printf...


?! parce qu'il n'a pas d'opérateurs de coercion vers const char* ?
une raison de plus (pour moi) de ne pas l'utiliser.


Je ne vois pas vraiment le rapport entre un operateur de conversion
automatique vers const char* et la compatibilité avec printf.


je ne vois pas non plus vraiment ce que Loïc a voulu dire ...
mais il n'a pas dit "compatible" (pour lequel je vois encore moins le
rapport) mais "utilisable".

"directement" voulait peut-être signifier sans utiliser un
getLeBouzinInterne() -- ça doit exister ça non ?

Sylvain.




1 2 3 4 5