Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Surcharge de l'operator

16 réponses
Avatar
condo4
Bonjour,

Je souhaite cr=E9er une classe pour cr=E9er des log.

Class SysLog

j'aimerai utiliser comme ceci :

SysLog a;
a<<"Chaine a logguer : "<<45;

elle ecrira dans un fichier
2007/03/06 12:10:30 Chaine a logguer : 45

Mais je ne sais pas comment m'y prendre, qu'elle operateur
surcharger....

Pour le moment, j'ai essayer =E7a, mais =E7a ne marche pas....

Quelqu'un peut-il m'eclairer sur la bonne fa=E7on pour surcharger << ???

Merci.



class LogSys{
public:
ofstream logfile;
LogSys();
~LogSys();
ostream& operator<<(ostream& flux);
};


LogSys::LogSys(){
logfile.open("c:\\Trace.log",ios::app);
}

LogSys::~LogSys(){
logfile.close();
}

ostream& LogSys::operator<<(const ostream& flux){
char now[31];
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo =3D localtime (&rawtime);
strftime(now,100,"%Y/%m/%d %H:%M:%S : ",timeinfo);
return logfile<<now<<flux<<endl;
}


@+Fab

6 réponses

1 2
Avatar
Michael DOUBEZ
Michael DOUBEZ writes:

"condo4" writes:

Bonjour,

Je souhaite créer une classe pour créer des log.

Class SysLog

j'aimerai utiliser comme ceci :

SysLog a;
a<<"Chaine a logguer : "<<45;
La technique habituelle consiste a creer ton propre streambuf. Un exemple

dans la FAQ. On doit pouvoir en trouver d'autres en faisant une recherche
dans les archives.

En général mais si il crée son streambuf il va avoir un log du type

2007-03-06 13:20:42 - Chaine a logguer :
2007-03-06 13:20:42 - 45




Comme James Kanze l'a fait remarqué dans un autre post, le log n'aura
pas cette tête là; c'est un autre problème que j'avais eu.

Il faut naturellement choisir ou on veut la chaine d'identification.
Quelques solutions:
- a chaque ligne
- a chaque flush
- avec un formatteur special.


Oui, j'avais essayé de faire ce genre de processing au moment du flush
mais les personnes ont l'habitude d'utiliser std::endl pour les
caractères de fin de ligne ce qui causait un flush du log alors qu'il
s'agissait d'un log sur plusieurs ligne.

Avec un minimum de discipline c'est effectivement praticable (ou alors
en considérant que les logs ne sont que sur une seule ligne, ce qui est
compréhensible).

Michael




Avatar
Sylvain
James Kanze wrote on 07/03/2007 10:05:

En général, j'évite des conversions implicites chaque fois que
je peux. Et en général, j'estîme que l'utilisation d'explicit
dans le cas des constructeurs à un seul paramètre doit être un
automatisme, dont on s'écarte uniquement quand on veut
explicitement une conversion implicite (ce qui doit être assez
rare).


"programmation défensive" comme tu dis, ça parait sain, en effet.

pourquoi recopier la struct tm sur la pile ?


Habitude. Dans la pratique, j'utilise prèsque toujours la
fonction non standard localtime_r, qui exige l'adresse du buffer
sur la pile (et qui marche aussi dans une contexte
multithreaded).


merci, ainsi qu'à Jean-Marc, pour ces explications, je suis d'accord
avec vous sur cette protection safe-thread (qui coute suffisamment peu
pour la retenir, même si une date fausse de qlq ms ne me paraissait pas
si grave) toutefois le problème plus génant (en multi threading) serait
l'injection désordonnée des informations.

sur ce point la simple écriture:
log << "bla" << i << endl;
n'est pas protégée.

l'écriture:
log.startRecord();
log << "bla" << i << endl;
log.endRecord();
protège le contenu du log mais au prix d'un assert.

est-ce qu'un string (mutable) "time-stampé" (constructeur s'injectant le
date/time courant et/ou méthode de 'reset') utilisé avec un ofstream
classique ne ferait pas l'affaire ?

timestring ts;
ts.clear();
log << (ts << "bla" << i << endl);
ts.clear();
log << (ts << "foo" << j << endl);

[...]
Encore, en général : il s'agit ici d'un type à sémantique de
valeur. Et les valeurs, en général, je ne les manipule pas à
travers les pointeurs.


ici (et on pourrait imaginer des cas semblables) le code ne manipule pas
lui-même ces valeurs, mais soit, cela apporte de la consistante sans
être négatif.

Sylvain.


Avatar
James Kanze
On Mar 7, 2:51 pm, Sylvain wrote:
James Kanze wrote on 07/03/2007 10:05:

En général, j'évite des conversions implicites chaque fois que
je peux. Et en général, j'estîme que l'utilisation d'explicit
dans le cas des constructeurs à un seul paramètre doit être un
automatisme, dont on s'écarte uniquement quand on veut
explicitement une conversion implicite (ce qui doit être assez
rare).


"programmation défensive" comme tu dis, ça parait sain, en effet.

pourquoi recopier la struct tm sur la pile ?


Habitude. Dans la pratique, j'utilise prèsque toujours la
fonction non standard localtime_r, qui exige l'adresse du buffer
sur la pile (et qui marche aussi dans une contexte
multithreaded).


merci, ainsi qu'à Jean-Marc, pour ces explications, je suis d'accord
avec vous sur cette protection safe-thread (qui coute suffisamment peu
pour la retenir, même si une date fausse de qlq ms ne me paraissait pas
si grave) toutefois le problème plus génant (en multi threading) sera it
l'injection désordonnée des informations.


Si la fonction que tu appelles appelle localtime avec un
paramère qui est, par exemple, le timestamp d'un fichier (qui
sont aussi des time_t sous Unix), la date risque d'être fausse
de plus que quelque millisecondes:-).

(Quant à la thread-safe : le localtime sous Posix n'est pas
thread safe, et l'appeler depuis plusieurs thread différents
donne un comportement indéfini.)

sur ce point la simple écriture:
log << "bla" << i << endl;
n'est pas protégée.

l'écriture:
log.startRecord();
log << "bla" << i << endl;
log.endRecord();
protège le contenu du log mais au prix d'un assert.


L'idée de base, c'est de faire quelque chose du genre :

log() << "bla" << i << 'n' ;

ou log() appelle startRecord(), et renvoie un temporaire (c'est
le but de mon OutputStreamWrapper) qui appelle endRecord() dans
le destructeur. Actuellement, c'est un peu fastidieux, à cause
des copies ; je gère un compteur d'instances dans mon
OutputStreamWrapper, par exemple, pour n'appeler endRecord() que
lorsque la dernière copie est détruite. Aussi, dans un
environement multi-thread, log() acquiert un lock, que le
destructeur libère.

En fait, mon streambuf vérifie dans endRecord() que le dernier
caractère sorti était bien 'n', et sinon, il l'ajoute. Aussi,
il fait un flush, pour que les informations dans le log soient
toujours à jour -- le log est extrèmement intéressant après un
core dump, alors que sans le flush, les informations les plus
intéressantes ne seraient probablement pas encore sur le disque.

Aussi, en guise de log(), j'utilise un macro LOG, qui appelle en
fait « log( __FILE__, __LINE__ ) » (dans le cas le plus
simple). Informations que j'ajoute à l'en-tête de ligne.

est-ce qu'un string (mutable) "time-stampé" (constructeur s'injectant le
date/time courant et/ou méthode de 'reset') utilisé avec un ofstream
classique ne ferait pas l'affaire ?

timestring ts;
ts.clear();
log << (ts << "bla" << i << endl);
ts.clear();
log << (ts << "foo" << j << endl);


S'il supporte <<, pourquoi pas ? Seulement, il exige un peu
plus de la part des programmeurs. D'après mon expérience, il
faut que le log soit ce qu'il y a de plus simple à utiliser ;
c'est déjà assez difficile à le faire utiliser quand c'est
simple.

--
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
James Kanze
On Mar 6, 2:36 pm, "condo4" wrote:
et est-ce qu'il ne serait pas possible d'heriter de ofstream ?


Certes. Mais toujours avec un streambuf spécial.

Je ne l'ai pas fait dans mon cas parce que je veux un objet
temporaire, afin de pouvoir intervenir aussi à la fin du
message. Or, pour être renvoyer comme temporaire, il faut que
le type soit copiable, ce que n'est pas std::ostream.

Du style :
using namespace std;

class LogSys: public ofstream{
public:
__ostream_type & operator<< (__streambuf_type *__sb);

};

__ostream_type & LogSys::operator<< (__streambuf_type *__sb){
char now[31];
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime (&rawtime);
strftime(now,100,"%Y/%m/%d %H:%M:%S : ",timeinfo);

//Et la on appel la fonction de l'ancetre avec now devant __sb ????
}


Je ne suis pas sûr de comprendre. Où et comment est-ce qu'on se
sert de cette classe ? On cherche à faire quelque chose du
genre :

log << "bla " << variable << std::endl ;

Si on veut agir à, disons, chaque 'n', un std::ostream
classique avec un streambuf spécial fait l'affaire. Si on veut
traiter les enrégistrements plus compliqués (genre plusieurs
lignes), la solution la plus simple pour l'utilisateur, c'est un
temporaire dont le constructeur démarre l'enrégistrement, et le
destructeur le termine proprement.

Note que si on accepte que l'enrégistrement, c'est la ligne, on
peut bien faire tout dans le streambuf. La fonction overflow
fait à peu près :

int overflow( int ch )
{
if ( isAtStartOfLine ) {
// prends le lock si multi-thread...
// insère l'en-tête...
}
dest->sputc( ch ) ;
if ( ch == 'n' ) {
dest->pubsync() ;
// libère le lock si multi-thread...
}
return ch ;
}

Mais je me méfierais un peu. Une exception dans quelque chose
du genre :
log << "bla " << v << 'n' ;
ne va pas faire beau. (Avec ma solution, cet enrégistrement
serait tronqué ; ce n'est pas vraiment beau non plus, mais
c'est un peu moins laid, quand même.)

--
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
Jean-Marc Desperrier
Sylvain wrote:
[...]
merci, ainsi qu'à Jean-Marc, pour ces explications, je suis d'accord
avec vous sur cette protection safe-thread (qui coute suffisamment peu
pour la retenir, même si une date fausse de qlq ms ne me paraissait pas
si grave) toutefois le problème plus génant (en multi threading) serait
l'injection désordonnée des informations.
[...]


Malheureusement je ne peux pas être précis, car ce n'était pas mon
programme, et je ne me souviens plus des détails, mais un collègue a une
fois identifié la source d'un problème de crash sur Solaris comme venant
juste de l'appel d'une fonction de temps non thread-safe.

Et pourtant on avait du mal à comprendre comment ça pouvait avoir pour
conséquence un crash ...

Avatar
James Kanze
On Mar 8, 9:30 pm, Jean-Marc Desperrier wrote:
Sylvain wrote:
[...]
merci, ainsi qu'à Jean-Marc, pour ces explications, je suis d'accord
avec vous sur cette protection safe-thread (qui coute suffisamment peu
pour la retenir, même si une date fausse de qlq ms ne me paraissait p as
si grave) toutefois le problème plus génant (en multi threading) se rait
l'injection désordonnée des informations.
[...]


Malheureusement je ne peux pas être précis, car ce n'était pas mon
programme, et je ne me souviens plus des détails, mais un collègue a une
fois identifié la source d'un problème de crash sur Solaris comme ven ant
juste de l'appel d'une fonction de temps non thread-safe.

Et pourtant on avait du mal à comprendre comment ça pouvait avoir pour
conséquence un crash ...


Ça dépend de l'implémentation. Dans la mesure où Posix n'exige
pas que la fonction soit réentrante (ce qui n'a pas de sens,
étant donné son interface), il n'y a pas de raison qu'elle le
soit. Alors, qui sait quel magouillage elle se pratique (et je
peux te dire d'expérience que ces fonctions-là, sous Solaris,
pratique plein de magouillages).

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


1 2