Modifier le comportement de "cout"

ou le re-ecrire

a marqué ce sujet comme résolu.

Bonjour,

J’ai un programme qui fait appelle à des "cout" pour l’affichage en console et des "ofstream" pour conserver des logs.

J’aimerais bien que les affichage console et les logs ressemblent à ça :

1
NomDeLaClasse : blablabla

Avec "NomDeLaClasse" qui soit automatiquement inséré.

En gros, j’aimerais pouvoir écrire une truc équivalent à "cout". Dans l’idée un truc de ce genre

1
USERcout = std::cout<<static_ClassName<<" : "

ou static_ClassName contient le nome de la classe en dur et est définis pour chaque classe.

Et j’aimerais faire de même pour les "ofstream" pour mes logs. Par contre j’ai aucune idée de comment faire… Et je suis sur qu’il y a un moyen même si c’est pas celui que je décris car je l’ai déjà vu dans des programmes.

Voila, si une âme charitable peut me donner une piste…

+0 -0

Personnellement j’ai toujours utilisé une bibliothèque externe quand je souhaitais des logs plus complexes et automatisées que ce qu’offrent les bibliothèques standards. Typiquement Google Log est une bibliothèque C++ très sympa pour ça, qui aide beaucoup pour le débogue.

+2 -0

Tu peux redéfinir l’opérateur << en implémentant la méthode operator<<.

1
2
3
4
5
std::ostream& operator<< (std::ostream& o, MaClasse c)
{
    o << "MaClasse : "; // Ce que tu veux écrire
    return o;
}

Ca ne modifie pas l’objet cout, mais ça modifie son comportement sur les objets de type MaClasse. Tu peux par exemple faire hériter tes objets à logger d’une interface ILoggable puis implémenter la surcharge de operator<< avec le type ILoggable.

+0 -0

Salut,

Mon C++ est un peu rouillé, mais quelque chose comme suit devrait faire l’affaire :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

class Logger
{
  std::string _name;
public:
  Logger(const std::string& name) : _name(name) {}

  template <typename T>
  std::ostream& operator<<(const T& arg)
  {
    return std::cout << _name << arg;
  }
};

int main(void)
{
  Logger log{"ma classe : "};
  log << "test" << "toto" << std::endl;
  return 0;
}

@A-312 c’est ça (entwanne idem mais en un peu moins bien )

J’avais pensé à surcharger « mais je ne comprenais pas comment… Une fois qu’on à la réponse ça parait simple … >_<

@Renault : glog ? Bon du coup j’avais commencé à regarder mais la solution de créer une classe spécial ça suffit, je veux juste faire des trucs basique.

@Gaah je comprend pas trop ce que tu vexu faire, mais j’ai l’impression que ca ne fonctionnerai pas (ou pas bien )

Avec "NomDeLaClasse" qui soit automatiquement inséré.

Vael

Je suis pas sûr d’avoir compris. NomDeLaClasse, c’est le nom de quelle classe ? Une classe que tu as défini ? Celle qui est en train d’imprimer le texte ? Si oui, il doit y avoir moyen de déduire le nom de classe à coup de typeid.

Celel qui imprime le texte.

Par exemple si j’ai un truc :

1
2
3
4
class UneClasse
{
void UneFonction() { std::cout<<"Plop"<<std::endl;}
}

J’ai envie d’avoir en console :

1
UneClasse : Plop

Avec la classe "Logger" proposé j’ai fais :

1
2
3
4
5
6
7
Logger UneClasse::UserCout(std::cout, "UneClasse : ");

class UneClasse
{
void UneFonction() { UserCout<<"Plop"<<std::endl;}
static Logger UserCout
}

Ça marche bien (après je sais pas si c’est propre…) C’est codé en dur mais bon je suis pas sur que ce soit un problème. Après si je peux faire pareille mais sans rajouter à chaque classe un membre Logger manuellement je prend aussi ^^

Si par exemple je fais hériter mes classes de la classe Logger et que dans Logger je fais :

1
2
3
4
   std::ostream & operator<<(T const & x)
    {
        return out << typeid(this).name() << x;
    }

ça devrait marcher ? (je ne peux pas tester là)

Personnellement j’ai toujours utilisé une bibliothèque externe quand je souhaitais des logs plus complexes et automatisées que ce qu’offrent les bibliothèques standards. Typiquement Google Log est une bibliothèque C++ très sympa pour ça, qui aide beaucoup pour le débogue.

Renault

Aujourd’hui, c’est spdlog qui déchire sur plus d’un aspect en C++. C’est simple/ergonomique, flexible, puissant, et très très rapide.

Sinon, autant on peut aller piocher automatiquement le nom d’une fonction avec des trucs non standards comme __PRETTY_FUNCTION__, autant pour les classes, il n’y a rien.

De plus, tu (OP) ne veux pas altérer le flux d’écriture pour le passer en mode "j’ajoute des trucs automatiquement" car ces machins pourraient s’insérer depuis ailleurs, et ce n’est pas ce que tu veux: tu veux faire du log débug qui instrumente les fonctions de tes classes.

Ce que j’ai déjà vu faire, c’est de l’abus de macros. Genre, une classe par unité de traduction (UT) (en gros, par .cpp), et une constante non exportée (via namespace anonyme) par UT. Et des macros pour vont utiliser cette constante pour les logs de débug.

Maintenant, par expérience, les noms de classes, c’est pas hyper utile. Il faut logguer les événements singuliers d’une façon compréhensible pour l’utilisateur (donc pas de bruit faisant référence au code (classes, fichiers sources…). Et le log débug, je préfère les __FILE__:__LINE__: message aux noms de classe, comme ça je peux directement ouvrir le bon fichier au bon endroit. Pour les logs debug, j’abuse de scoped loggers, en gros, c’est le RAII appliqué aux logs pour logguer automatiquement et systématiquement les sorties de portée. Avec le C++17, on pourra même savoir indiquer automatiquement si la sortie est normale ou sur exception. Ca, ça sera cool.

PS, le RTTI permet effectivement de choper le nom de la classe associée à une variable (pas sûr que cela soit très utile pour les fonctions membres statiques), mais c’est manglé et inutilisable directement. Il faut faire le boulot de c++filt pour rendre le nom exploitable. Il y a une bibliothèque non standard ni portable, dont le nom m’échappe, qui permet de faire ça. Bref, il est plus simple de passer par namespace { char const[] MONMODULE="FooBar"; }. Avec un bon environnement de dév, cela peut même se générer automatiquement à partir du nom du fichier .cpp.

PPS: J’ai écris/corrigé le framework de logs de pas mal de projets sur lesquels j’ai bossé. Il y a plein de choses à savoir pour se multithréader correctement, pour ne pas payer si le niveau de trace est inférieur au niveau du log que l’on veut émettre, pour avoir une syntaxe simple, etc. Toutes ces raisons me poussent à préférer me tourner vers des bibliothèques externes, et pour l’instant, c’est spdlog la première que j’ai employée sans que je ne râle comme un putois (p.ex. je tends à tirer à vu sur log4cxx et toutes les libs qui exposent des pointeurs bruts/nus. NB: je n’ai pas eu l’occasion de regarder glog,

PS, le RTTI permet effectivement de choper le nom de la classe associée à une variable (pas sûr que cela soit très utile pour les fonctions membres statiques), mais c’est manglé et inutilisable directement.

lmghs

Il y a la fonction abi::__cxa_demangle dans le header cxxabi.h pour démangler, mais ce n’est pas standard (d’ailleurs MSVC ne la supporte pas).

Lu’!

Je suis plutôt d’accord avec @Renault et @lmghs : tu es actuellement en train de te faire chier de redévelopper un truc qui existe déjà moultes fois, moins bien et ça te prends du temps. Si tu prends spdlog, tout ce que ça t’aurais pris comme temps, c’est copier les fichiers headers (puisqu’elle est header-only) dans ton projet et tu serais déjà en train de continuer à bosser sur ton projet de base.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte