OVH Cloud OVH Cloud

Définir une classe log et l'utiliser comme cerr

32 réponses
Avatar
Cornu Nicolas
Bonjour,

J'aimerais définir une classe log
et l'utiliser comme cerr.

log << "erreur";

J'ai éssayer plusieurs surcharge de l'opérateur <<
en définissant à l'extèrieur de la classe log une surcharge
sans succès


Merci d'avance,

Cornu Nicolas

10 réponses

1 2 3 4
Avatar
kanze
Fabien LE LEZ wrote:
On Sun, 10 Apr 2005 17:09:44 +0200, "Cornu Nicolas"
:

J'ai besoin de créer une classe virtuelle pure.


Et ?

Si j'ai bien compris, ILogger (encore un Java-isme, ce nom
:-/)


Je ne crois pas. Peut-être un Microsoft-isme (encore que la
seule fois que j'ai vue la convention était sous Solaris), mais
certainement pas Java.

est une classe de base, non instanciable, et Logger est une
classe instanciable dérivée de ILogger.

Dans ce cas, un code correct et représentatif serait :

void f (ILogger& log)
{
log << "Hello world!" << endl;
}

int main()
{
Logger mon_logger;
f (mon_logger);
}


Et sa fonction templatée ? Qu'est-ce que tu en fais ?

Dans ce cas-ci, il faut bien passer par un operator<< libre (ou
au moins non virtuel), templaté, qui fait appelle à la classe de
log pour obtenir le flux voulu :

template< typename T >
ILogger&
operator<<( ILogger& dest, T const& value )
{
dest.stream() << value ;
}

(Mais on pourrait bien imaginer que ILogger::stream() renvoie un
pointeur, NULL dans le cas où le log est inhibé, et qu'on teste
avant de s'en servir. De même, le stream en question utilisera
prèsque sûrement un streambuf filtrant, afin de detecter les
débuts et les fins de lignes, etc., pour insérer du texte
supplémentaire, flusher les buffers, etc.)

Mais normalement, on ferait dériver Logger (ou ILogger) de
ostream, pour pouvoir écrire :

void f (ostream& log)
{
log << "Hello world!" << endl;
}

int main()
{
Logger mon_logger;
f (mon_logger);

f (cout);
f (cerr);
}


Je ne crois pas trop. Qu'est-ce que ça nous apporte par rapport
à l'utilisation directe du flux ?

--
James Kanze GABI Software
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
kanze
Fabien LE LEZ wrote:
On Sun, 10 Apr 2005 17:44:31 +0200, "Cornu Nicolas"
:

Je ne suis pas un adepte de Java mais plutot de .NET


C'est la même chose. La principale différence est l'éditeur.

Je préfère suffixer mes interface avec un I :)


"Préfixer", tu veux dire ?

Note que le terme "interface" est peu courant en C++.


D'où est-ce que tu as ça ? C'est un modèle de conception
extrèmement répandu.

Le concept même m'est peu familier -- j'ai rarement l'occasion
de faire des classes ne contenant que des fonctions virtuelles
pures.


Alors, tu laisses à côté une des forces de C++. Autant faire du
C, carrément:-). Je dirais même que la plupart du temps que je
dérive, c'est une interface.

Au moins que tu veux dire littéralement. Les interfaces en C++
utilisent souvent les conceptes de la programmation par contrat,
avec des fonctions non-virtuelles publiques qui renvoient à la
fonction virtuelle privée ou protégée. Mais c'est un détail de
l'implémentation ; ça reste une interface quand même.

--
James Kanze GABI Software
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
kanze
Olivier Azeau wrote:
Fabien LE LEZ wrote:
On Sun, 10 Apr 2005 17:44:31 +0200, "Cornu Nicolas"
:

Je préfère suffixer mes interface avec un I :)


"Préfixer", tu veux dire ?

Note que le terme "interface" est peu courant en C++. Le
concept même m'est peu familier -- j'ai rarement l'occasion
de faire des classes ne contenant que des fonctions
virtuelles pures.


J'aurais plutôt dit :
le terme "interface" est peu courant sur f.c.l.c++


C'est parce qu'on parle ici surtout des détails de
l'implémentation, et non de la conception. L'expression est
assez répandue. Je ne sais pas ce qui a piqué Fabien cette
fois-ci, mais je vois mal un programmeur C++ qui ne connaît pas
le concepte, et qui ne s'en sert pas régulièrement.

Au niveau de l'implémentation C++, évidemment, on parle plutôt
d'une classe abstraite.

Enfin, on se sert parfois aussi du mot avec une signification un
peu différente -- l'interface, c'est la partie de la classe que
l'utilisateur voit, ou qu'il doit voir afin de se servir de la
classe. Le contraire de l'implémentation, donc.

Évidemment, en Java, il y a encore plus de confusion, parce
qu'ils ont un mot clé interface, dont l'utilisation ne
correspond pas forcément à une des utilisations ci-dessus non
plus.

--
James Kanze GABI Software
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
kanze
Fabien LE LEZ wrote:
On Sun, 10 Apr 2005 16:27:18 +0200, "Cornu Nicolas"
:

int main(int argc, char *argv[])
{
ILogger *log = new Logger();
log->write("132");
log << "123";
std::string s;
std::cin >> s;
return EXIT_SUCCESS;
}


Note que le code ci-dessus est totalement incorrect, puisque
"log" est créé par new et jamais détruit.


Ce qui n'est pas incorrect si c'est ça qu'on veut. Si on veut
pouvoir se servir du log dans les destructeurs des objets
statiques, c'est probablement mieux qu'on ne le détruit pas
avant:-). (Dans mon propre code, le log est typiquement un
singleton dont on ne detruit jamais l'instance.)

--
James Kanze GABI Software
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
kanze
Fabien LE LEZ wrote:
On Sun, 10 Apr 2005 16:27:18 +0200, "Cornu Nicolas"
:

int main(int argc, char *argv[])
{
ILogger *log = new Logger();
log->write("132");
log << "123";
std::string s;
std::cin >> s;
return EXIT_SUCCESS;
}


Note que le code ci-dessus est totalement incorrect, puisque
"log" est créé par new et jamais détruit.


Ce qui n'est pas incorrect si c'est ça qu'on veut. Si on veut
pouvoir se servir du log dans les destructeurs des objets
statiques, c'est probablement mieux qu'on ne le détruit pas
avant:-). (Dans mon propre code, le log est typiquement un
singleton dont on ne detruit jamais l'instance.)

--
James Kanze GABI Software
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
kanze
Cornu Nicolas wrote:
J'ai beosin de redefinir la fonction dans les classe filles de
ILogger je prévoit de faire un FileLogger, DatabaseLogger ...


Je ne crois pas. Régarde bien la conception des flux ; c'est un
modèle à suivre pour ce genre de problème.

En fait, tu as plusieurs choses différentes à considérer, et il
vaut mieux les considérer séparamment (c'est la « separation of
concerns » en anglais, mais je ne trouve pas de bonne
équivalence en français.

-- tu dois formatter les données en texte,
-- tu dois les écrire quelque part, et
-- éventuellement, il faut des traitements spéciaux par rapport
au flux classiques : ajouter un timestamp en tête de chaque
ligne, forcer un flush du buffer à la fin, etc.

Normalement, le formattage des données dépend du type des
données, et nullement de la destination finale. (Dans certains
cas, il peut aussi varier selon la destination ; j'en reviens
plus bas.) C'est donc que la fonction qui l'effectue doit être
selectionner d'après le type des données, et uniquement le type
des données. Soit statiquement, par la résolution du surcharge,
soit dynamiquement, mais alors, il faudrait que la fonction soit
une fonction membre virtuelle de l'objet qu'on va écrire (et non
de Log). Ce qui n'est pas sans problèmes si l'objet est un
int:-).

On remarque que dans les flux classique, les opérateurs << ne
sont même pas forcément membre, et que même les membres ne sont
pas virtuels.

Ensuite, il faut écrire le résultat du formattage quelque part.
Dans les flux classiques, c'est le rôle des streambuf -- une
classe abstraite dont on dérive selon la destination et la
source des caractères. La norme en fournit déjà deux dérivées :
filebuf et stringbuf. Mais c'est assez rare qu'ils suffisent, et
prèsque toutes les applications que j'ai écrit en comporte des
streambuf spéciaux : pour des sockets, pour des données en base
de données, etc.

Souvent, on veut aussi filtrer les données après formattage --
ajouter des choses, etc. -- les envoyer à plusieurs
destinations, ou exécuter des actions particulières à certains
moments (l'écriture en base de données, un flush du buffer,
l'envoie en tant qu'email...). C'est prèsque toujours le cas,
d'ailleurs, dans le cas des logs. Pour ça, on utilise un
concepte qui s'appelle un streambuf filtrant. C'est un streambuf
qui s'occupe des ces choses spéciales, puis renvoie les données
qui en résulte à un autre streambuf.

En tous les cas, c'est de streambuf qu'on dérive ; on ne touche
pas aux opérateurs <<, qui eux reste ce qu'ils sont, et qui se
servent des fonctions du streambuf pour sortir les caractères
générés.

En ce qui concerne les traitements spéciaux d'un log, beaucoup
se laisse réaliser au moyen des streambuf specialisé. (Sinon, il
y a des possibilités des wrappers plus ou moins complexes, mais
je crois que ça déborderait de loin ce qu'il te faut dans un
premier temps.)

En gros, ce qui va distinguer ton FileLogger de ton
DatabaseLogger, c'est seulement comment ils initialise les
streambuf du flux qu'ils offrent pour le log ; un
DatabaseStreambuf, par exemple, ferait peut-être :

int DatabaseStreambuf::overflow( int ch )
{
switch ( ch ) {
case 'n' :
writeBufferToDatabase( buffer ) ;
if ( error ) {
ch = EOF ;
}
buffer = "" ;
break ;

case EOF :
break ;

default :
buffer += ch ;
break ;
}
return ch ;
}

Pour un fichier, on se servirait en général d'un streambuf
filtrant (pour s'assurer d'un flush lors du 'n') qui renvoie à
un filebuf.

--
James Kanze GABI Software
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
kanze
Cornu Nicolas wrote:
Si je veux pouvoir utiliser

log << log_warning << "warning";

et que log_warning sois comme hex et oct pour les flux de la stl

cout << hex << 16;

que ca ajoute "WARNING" à un flux


Et alors, sans doute un log_error, un log_info, un log_fatal...
En somme, chaque utilisation du log doit s'accompagne avec son
niveau.

Alors, ne serait-il pas mieux de faire :

log( log_warning ) << "warning" ;

Du coup, la fonction log() renvoie une référence au flux, et le
tour est joué.

--
James Kanze GABI Software
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
Laurent Deniau
Cornu Nicolas wrote:
Bonjour,

J'aimerais définir une classe log
et l'utiliser comme cerr.

log << "erreur";


clog ne the convient pas? c'est standard...

a+, ld.

Avatar
Fabien LE LEZ
On 11 Apr 2005 02:17:46 -0700, :

Ce qui n'est pas incorrect si c'est ça qu'on veut.


Je considère le code proposé comme incorrect.
Le même code, avec le commentaire qui va bien pour expliquer l'absence
de delete, peut être considéré comme correct.


--
;-)

Avatar
kanze
Fabien LE LEZ wrote:
On 11 Apr 2005 02:17:46 -0700, :

Ce qui n'est pas incorrect si c'est ça qu'on veut.


Je considère le code proposé comme incorrect.


Moi pas forcément.

Le même code, avec le commentaire qui va bien pour expliquer
l'absence de delete, peut être considéré comme correct.


Je suis d'accord qu'il vaut mieux indiquer que l'absence de
delete est intentionnelle. En revanche, cette indication ne
prend que rarement la forme d'un commentaire, ou sinon, c'est un
commentaire indirect, du genre :

// ...
// Modèles de conception :
// -- Singleton
// ...

Dans la deccription de la classe.

Plus généralement, si le new fait partie de l'initialisation
d'une variable statique, ou d'une variable vers le début de
main, en dehors de toute boucle, je ne m'attends pas forcément à
trouver un delete. D'omettre le delete sans autre indication
n'est pas ce que je considère bon style dans une application
industrielle, mais moi aussi, quand je poste des bouts de code
ici, je ne les accompagne pas forcement de tout le commentaire
qu'il y aurait dans la production. C'est pareil en ce qui
concerne la gestion des erreurs, et sans doute d'autres aspects
aussi.

--
James Kanze GABI Software
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 3 4