IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Concevoir une classe de sortie avec ostream


Sujet :

C++

  1. #1
    Membre Expert
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Par défaut Concevoir une classe de sortie avec ostream
    Bonjour,

    j'aimerais écrire une classe me permettant de gérer des sorties, sur écran ou sur fichier, formatées pour des besoins spécifiques (en particulier des log files).

    Pour le moment, on utilise la sortie standard pour afficher un objet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::cout << objet << std::endl;
    Je souhaite définir une classe, disons Output, me permettant d'écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    output << objet << std::endl;
    pour toute instance output de Output.
    Je veux également pouvoir indiquer en début de programme si output est un fichier (ofstream) ou la sortie standard (cout).
    Je veux également être en mesure d'indiquer des préférences d'écriture à la manière de setprecision (en particulier des niveaux de verbosité).

    Idéalement, je voudrais que Output soit un ostream mais je me demande si tenter d'hériter proprement de ostream est vraiment une bonne idée.

    Quelle est la meilleure approche selon vous?

    Merci pour votre aide!

  2. #2
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 053
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 053
    Billets dans le blog
    141
    Par défaut
    Bonjour,

    Pourquoi ne pas utiliser ostream directement ? Ainsi cela peut être un fichier, ou std::cout, ou autre
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  3. #3
    Membre Expert
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Par défaut
    Bonjour LittleWhite,

    merci pour ta réponse!
    Qu'entends-tu par directement?

  4. #4
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 053
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 053
    Billets dans le blog
    141
    Par défaut
    Utiliser un ostream, sans même l'hériter ni rien, une instance de ostream.
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  5. #5
    Membre Expert
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Par défaut
    D'accord, je comprends.
    Je ne souhaite pas utiliser directement ostream pour deux raisons essentielles :
    1. je travaille dans un environnement parallèle et je souhaite pouvoir écrire des données distribuées dans un seul fichier;
    2. je prévois d'utiliser un singleton pour gérer mes fichiers log.

  6. #6
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 053
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 053
    Billets dans le blog
    141
    Par défaut
    N'héritez pas std::ostream (les classes de la std ne s'héritent pas vraiment). Encapsulez la dans une autre classe
    Pour le 2, pourquoi ne pas utiliser glog ?
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  7. #7
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 287
    Billets dans le blog
    2
    Par défaut
    Bonjour,

    lorsque le système de log devient complexe (watchdog), la stratégie classique (log4j, log4cpp, etc.) consiste à utiliser un appender. L'appender c'est une classe qui va gérer l'output.
    En c++, une façon élégante de faire consiste à utiliser les templates. Par exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    namespace bs = boost::serialization;
    namespace bg = boost::date_time::gregorian;
     
    class MyLogger
    {
    public: 
       template <typename AppenderType>
       static Log( const std::string & message )
       {
          mutex_.lock();
          AppenderType::Write( gr::to_iso_string( gr::day_clock::local_day() ) + message );
          mutex_.unlock();
       }
     
    private:
       static boost::mutex mutex_;
    };
    Le code ci-dessus est un exemple, ce n'est pas du code fonctionnel. Il n'y a pas de niveau d'importance, j'ai tout mis en statique, même l'utilisation de l'appender, juste parce que c'est plus court à écrire, etc.

    L'avantage c'est que tu peux définir tous les appenders que tu veux. Tu peux parfaitement faire un appender qui écrit dans la sortir standard, et dans une base de donnée, et dans un channel IRC avec de la coloration syntaxiques, etc, etc. De plus, à chaque fois que vous déciderez de changer le comportement du logger, il suffira de créer un nouvel appender: donc pas besoin de toucher au reste du code.

  8. #8
    Membre Expert
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Par défaut
    Bonsoir,
    merci pour vos réponses à tous les deux.

    Je ne peux pas utiliser glog car je travaille dans un code parallélisé et que je ne souhaite évidemment pas ouvrir un fichier par processeur.
    Je ne peux pas non plus utiliser log4cpp car le code est parallélisé avec MPI.

    Le concept d'Appender a l'air très intéressant.
    Malgré mes recherches sur les loggers, je ne suis pas tombé dessus.
    Donc merci beaucoup!

    Hériter de la STL n'est à ma connaissance pas conseillé à cause de la présence de destructeurs non virtuels.
    Sauf qu'il y apparemment des classes de streams et de buffers qui ont été faites pour servir de classes de base.
    Cela dit, cela a l'air tout de même assez ardu à utiliser.

    J'essaye de réfléchir à une forme d'encapsulation qui pourrait répondre à mes besoins.

  9. #9
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    As-tu jeté un œil à Boost.Log ?

  10. #10
    Membre Expert
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Par défaut
    Bonjour,
    à ma connaissance, boost.log ne permet pas non plus d'écrire dans un unique fichier avec plusieurs processeurs sous MPI.
    J'apprendrai sûrement beaucoup de choses en regardant ce qui est fait dans boost mais je ne crois pas que cela réponde directement à mon besoin.

  11. #11
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 391
    Par défaut
    Généralement, pour personnaliser la sortie d'un ostream sans avoir à réimplémenter tout le code de formatage du texte, on fait une classe dérivée de streambuf (ou son template), et on utilise la méthode rdbuf() d'un ostream ordinaire.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  12. #12
    Membre Expert
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Par défaut
    Bonjour Medinoc,


    merci beaucoup pour ta réponse!

    J'ai trouvé des documents intéressants sur le sujet mais il faut prendre le temps de les comprendre.
    C'est un peu déroutant, je m'attendais à hériter de ostream ou d'une classe de base, pas du tout de streambuf.
    Je suis plus que débutant dans le buffering.

    Pour ceux que le sujet intéresse, les documents suivants m'ont semblé pertinents :
    - chapitre 38 de la documentation Apache,
    - chapitre 39 de la documentation Apache,
    - un extrait du livre de Angelika Langer (son livre m'a l'air intéressant).

  13. #13
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 391
    Par défaut
    En fait, le buffer d'un ostream, c'est plus ou moins ça "sortie". Le ostream remplit tout seul le buffer, et appelle une méthode du streambuf quand c'est plein ou qu'une demande de flush (comme celle contenue dans endl) est invoquée.

    Il me semble qu'en fait, un ofstream, c'est juste un ostream avec un streambuf spécial qui écrit dans le fichier voulu.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  14. #14
    Membre Expert
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Par défaut
    Oui, c'est ce que j'ai (très grossièrement) compris.
    Pour changer le comportement d'un ostream, il suffit normalement de lui associer un streambuf personnalisé.
    Il reste à mettre en pratique.
    Je compte le faire cette semaine et je vous enverrai un petit exemple simple pour illustrer le principe.

  15. #15
    Expert confirmé

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Généralement, pour personnaliser la sortie d'un ostream sans avoir à réimplémenter tout le code de formatage du texte, on fait une classe dérivée de streambuf (ou son template), et on utilise la méthode rdbuf() d'un ostream ordinaire.
    Generalement on fait un ostream qui s'occupe d'initialiser le streambuf comme il faut (et eventuellement fournit quelques api en +).

    J'avais ecrit jadis un exemple pour la FAQ de fr.comp.lang.c++. Le voici:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
     
    #include <stdio.h>
    #include <assert.h>
     
    #include <iostream>
    #include <streambuf>
     
    // streambuf minimal encapsulant un FILE*
    //    - utilise les tampons de FILE donc n'a pas de tampon interne en
    //      sortie et a un tampon interne de taille 1 en entree car l'interface
    //      de streambuf ne permet pas de faire moins;
    //    - ne permet pas la mise en place d'un tampon
    //    - une version plus complete devrait permettre d'acceder aux
    //      informations d'erreur plus precises de FILE* et interfacer aussi
    //      les autres possibilites de FILE* (entre autres synchroniser les
    //      sungetc/sputbackc avec la possibilite correspondante de FILE*)
     
    class FILEbuf: public std::streambuf
    {
    public:
     
      explicit FILEbuf(FILE* cstream);
      // cstream doit etre non NULL.
     
    protected:
     
      std::streambuf* setbuf(char_type* s, std::streamsize n);
     
      int_type overflow(int_type c);
      int      sync();
     
      int_type underflow();
     
    private:
     
      FILE*    cstream_;
      char     inputBuffer_[1];
    };
     
    FILEbuf::FILEbuf(FILE* cstream)
      : cstream_(cstream)
    {
      // le constructeur de streambuf equivaut a
      // setp(NULL, NULL);
      // setg(NULL, NULL, NULL);
      assert(cstream != NULL);
    }
     
    std::streambuf* FILEbuf::setbuf(char_type* s, std::streamsize n)
    {
      // ne fait rien, ce qui est autorise.  Une version plus complete
      // devrait vraissemblablement utiliser setvbuf
      return NULL;
    }
     
    FILEbuf::int_type FILEbuf::overflow(int_type c)
    {
      if (traits_type::eq_int_type(c, traits_type::eof())) {
        // la norme ne le demande pas exactement, mais si on nous passe eof
        // la coutume est de faire la meme chose que sync()
        return (sync() == 0
            ? traits_type::not_eof(c)
            : traits_type::eof());
      } else {
        return ((fputc(c, cstream_) != EOF)
            ? traits_type::not_eof(c)
            : traits_type::eof());
      }
    }
     
    int FILEbuf::sync()
    {
      return (fflush(cstream_) == 0
          ? 0
          : -1);
    }
     
    FILEbuf::int_type FILEbuf::underflow()
    {
      // Assurance contre des implementations pas strictement conformes a la
      // norme qui guaranti que le test est vrai.  Cette guarantie n'existait
      // pas dans les IOStream classiques.
      if (gptr() == NULL || gptr() >= egptr()) {
        int gotted = fgetc(cstream_);
        if (gotted == EOF) {
          return traits_type::eof();
        } else {
          *inputBuffer_ = gotted;
          setg(inputBuffer_, inputBuffer_, inputBuffer_+1);
          return traits_type::to_int_type(*inputBuffer_);
        }
      } else {
        return traits_type::to_int_type(*inputBuffer_);
      }
    }
     
    // ostream minimal facilitant l'utilisation d'un FILEbuf
    // herite de maniere privee de FILEbuf, ce qui permet de s'assurer
    // qu'il est bien initialise avant std::ostream
     
    class oFILEstream: private FILEbuf, public std::ostream 
    {
    public:
      explicit oFILEstream(FILE* cstream);
    };
     
    oFILEstream::oFILEstream(FILE* cstream)
      : FILEbuf(cstream), std::ostream(this)
    {
    }
     
    // istream minimal facilitant l'utilisation d'un FILEbuf
    // herite de maniere privee de FILEbuf, ce qui permet de s'assurer
    // qu'il est bien initialise avant std::istream
     
    class iFILEstream: private FILEbuf, public std::istream
    {
    public:
      explicit iFILEstream(FILE* cstream);
    };
     
    iFILEstream::iFILEstream(FILE* cstream)
      : FILEbuf(cstream), std::istream(this)
    {
    }
     
    // petit programme de test
    #include <assert.h>
    int main(int argc, char* argv[])
    {
      FILE* ocstream = fopen("result", "w");
      assert (ocstream != NULL);
      oFILEstream ocppstream(ocstream);
      ocppstream << "Du texte";
      fprintf(ocstream, " melange");
      fclose(ocstream);
      FILE* icstream = fopen("result", "r");
      assert (icstream != NULL);
      iFILEstream icppstream(icstream);
      std::string word1;
      std::string word2;
      icppstream >> word1;
      icppstream >> word2;
      char buf[1024];
      fgets(buf, 1024, icstream);
      std::cout << "Got :" << word1 << ':' << word2 << ':' << buf << '\n';
    }

Discussions similaires

  1. [Templates] Comment concevoir ses états de sortie avec PHP (Reports) ?
    Par tarekos dans le forum Bibliothèques et frameworks
    Réponses: 1
    Dernier message: 10/02/2009, 15h04
  2. Réponses: 3
    Dernier message: 02/08/2008, 06h46
  3. créer une class de connexion avec la base de donnée
    Par john_wili dans le forum Débuter avec Java
    Réponses: 2
    Dernier message: 19/07/2008, 00h10
  4. Implémentation d'une classe dérivant de std::ostream
    Par three minute hero dans le forum SL & STL
    Réponses: 1
    Dernier message: 08/07/2008, 14h07
  5. Réponses: 13
    Dernier message: 02/02/2005, 00h21

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo