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

Langage C++ Discussion :

Flux & redéfinition de l'opérateur <<


Sujet :

Langage C++

  1. #1
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut Flux & redéfinition de l'opérateur <<
    Bonjour,

    J'ai une classe dont l'un des membres est lié à un flux. Celui-ci peut soit être un ofstream, ou cout.

    Le premier problème est que le premier doit être un membre à part entière, et le second ne peut être qu'une référence.

    Plutôt que de spécialiser ma classe (assez grosse) pour chacun de ces deux cas, je définis un membre vers un wrapper qui lui sera spécialisé.

    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
    template <class T>
    struct StreamWrapper;
     
    template <>	// fichier
    struct StreamWrapper<std::ofstream>
    {
    	typedef std::ofstream StreamType;
     
    	StreamWrapper(std::string& outputFilePath) : outputStream(outputFilePath) { }
    	std::ofstream	outputStream;
    };
     
    template <>	// console
    struct StreamWrapper<std::ostream>
    {
    	typedef std::ostream StreamType;
     
    	StreamWrapper(std::ostream& stream) : outputStream(stream) { }
    	std::ostream&	outputStream;
    };
     
    template <class T>
    struct A
    {
        ...
        StreamWrapper<Stream> outStream;
        ...
    };
    J'ai par la suite voulu faire une surcharge de l'opérateur << :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <class T, typename V>
    StreamWrapper<T>& operator<<(StreamWrapper<T>& stream, const V& value)
    {
    	return (stream << value);
    }
    mais l'utilisation de std::endl m'a posé un problème.
    D'après ce que j'ai pu glaner sur le web, il faut faire en plus quelque chose du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template <>
    StreamWrapper<std::ofstream>& operator<<(StreamWrapper<std::ofstream>& stream, std::ofstream& (*manip)(std::ofstream&))
    {
    	manip(stream.outputStream);
     
    	return stream;
    }
    Mais bon, ça ne marche toujours pas. Il y un problème de résolution de template à la compilation.

    Quelqu'un serait-il assez charitable pour me donner un coup de pouce ?
    Merci.

    PS : Que pensez-vous de ce livre ?

  2. #2
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Bonsoir,

    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
     
    #include<iostream>
     
    namespace N
    {
     
    template<class T>
    struct A
    {
        A(T& out)
            : out(out) {}
        T& out;
    };
     
    template<class T, class U>
    A<T>& operator<<(A<T>& a, const U& u)
    { a.out << u; return a; }
     
    template<class T>
    A<T>& operator<<(A<T>& a, T& (*m)(T&))
    { a.out << m; return a; }
     
    }
     
    int main()
    {
        N::A<std::ostream> a(std::cout);
        a << 0 << std::endl;
    }
    Fonctionne parfaitement.

  3. #3
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct A
    {
        A(T& out)
            : out(out) {}
        T& out;
    };
    out n'est une référence que si T est std::cout. Si c'est un fichier, c'est une instance de ifstream. D'où mon utilisation d'un wrapper. Et c'est peut-être là que ça complique les choses.

  4. #4
    Membre habitué
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 118
    Points : 158
    Points
    158
    Par défaut
    2 problèmes :

    - Tu fais une mauvaise utilisation du mot-clé typename.

    - Ta spécialisation totale ne matchait pas la fonction générique.

    Une correction potentielle :
    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
    template <class T, typename V>
    StreamWrapper<T>& operator<<(StreamWrapper<T>& stream,  const V& value)
    {
    	return (stream << value);
    }
     
    typedef std::ofstream& (*func_ptr)(std::ofstream&); //voir la note
     
    template<>
    StreamWrapper<std::ofstream>& operator<<(StreamWrapper<std::ofstream>& stream,  const func_ptr& manip)
    {
    	manip(stream.outputStream);
     
    	return stream;
    }
    NOTE : à tester avec le mot clé using

  5. #5
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Citation Envoyé par backlash Voir le message
    - Tu fais une mauvaise utilisation du mot-clé typename.
    C'était un reliquat d'une aute tentative où le typename était nécessaire...
    Je les ai viré dans mon 1er emssage. Merci de la remarque.

    Citation Envoyé par backlash Voir le message
    - Ta spécialisation totale ne matchait pas la fonction générique.
    Je n'ai pas compris ce que tu as changé, mis à part l'utilisation du typedef.

  6. #6
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Ma classe A c'était ta classe StreamWrapper :
    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
     
    namespace N
    {
     
    template<class T>
    struct SW;
     
    template<class charT, class traits>
    struct SW<std::basic_ostream<charT,traits> >
    {
        typedef 
            std::basic_ostream<charT,traits>& 
            (*manip)(std::basic_ostream<charT,traits>&);
        std::basic_ostream<charT,traits>& out;
        SW(std::basic_ostream<charT,traits>& out)
            : out(out) {}
    };
     
    template<class charT, class traits>
    struct SW<std::basic_ofstream<charT,traits> >
    {
        typedef 
            std::basic_ostream<charT,traits>& 
            (*manip)(std::basic_ostream<charT,traits>&);
        std::basic_ofstream<charT,traits> out;
        template<class S>
        SW(S out)
            : out(out) {} 
    };
     
    template<class T, class U>
    SW<T>& operator<<(SW<T>& sw, const U& u)
    { sw.out << u; return sw; }
     
    template<class T>
    SW<T>& operator<<(SW<T>& sw, typename SW<T>::manip m)
    { sw.out << m; return sw; }
     
    }

  7. #7
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Je n'aime pas trop cette idée de classe parfois possédant un objet, parfois n'en possédant pas. Pourquoi ne pas simplement stocker une référence à un ostream dans ta classe, et gérer la durée de vie du fstream ailleurs, par exemple en tant que variable locale à une fonction genre main ?
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  8. #8
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    C'est justement ce que je veux éviter.

    J'ai plusieurs instanciations de ma classe la plus dérivée, avec variation de la valeur du template. Chaque instance doit se voir attachée un fichier de sortie qui lui est propre.

    Ça ressemble à quelque chose comme ça (BaseFunctor est un type Erasure) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //typedef std::ostream OutStream;
    typedef std::ofstream OutStream;
     
    typedef BaseFunctor<OutStream> StreamedFunctor;
     
    std::array<std::shared_ptr<BaseFunctor<OutStream>>, 4> fctArray;
     
    	fctArray[0] = std::shared_ptr<StreamedFunctor>(new Functor_1<float, OutStream>(n, std::string("fichier1.csv")));
    	fctArray[1] = std::shared_ptr<StreamedFunctor>(new Functor_2<float, OutStream>(n, std::string("fichier2.csv")));
    	fctArray[2] = std::shared_ptr<StreamedFunctor>(new Functor_3<float, OutStream>(n, std::string("fichier3.csv")));
    	fctArray[3] = std::shared_ptr<StreamedFunctor>(new Functor_4<double, OutStream>(n, std::string("fichier4.csv")));
    L'intérêt, c'est que je n'ai qu'à jouer du commentaire sur les 2 premières lignes. Si je commente la première, les noms de fichiers sont utilisés pour créer lesdits fichiers et les remplir. Si je commente la seconde, tout va vers std::cout, et le second argument n'est pas utilisé.

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

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Flob90, je te remercie beaucoup !

    J'ai réussi à appliquer ce que tu m'as indiqué dans une version allégée (bien qu'encore lourde) de mon code, mais je n'arrive pas encore à le faire compiler dans mon code d'origine. Je ne désespère pas d'y arriver.

Discussions similaires

  1. Redéfinition de l'opérateur new
    Par Khan34 dans le forum C++
    Réponses: 15
    Dernier message: 05/07/2012, 16h27
  2. Problème redéfinition de l'opérateur []
    Par scary dans le forum Débuter
    Réponses: 2
    Dernier message: 05/03/2010, 11h00
  3. redéfinition de l'opérateur ==
    Par zackrel dans le forum C++
    Réponses: 6
    Dernier message: 24/04/2006, 20h34
  4. redéfinition des opérateurs en C++
    Par apan dans le forum C++
    Réponses: 11
    Dernier message: 27/03/2006, 15h58
  5. Redéfinition / Surdéfinition d'opérateur
    Par slate dans le forum C++
    Réponses: 12
    Dernier message: 17/02/2006, 02h17

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