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 :

Problème de généricité avec les templates


Sujet :

C++

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Par défaut Problème de généricité avec les templates
    Bonjour,

    Pour la conception d'une application d'affichage de données issues de trames réseaux, je souhaiterais faire quelque chose d'assez générique.

    Je suis donc parti sur l'utilisation de templates et ai procédé de la sorte :

    Une classe pour représenter des données de type traces :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class QTraceData
    {
      public :
        quint8 m_type;
        QString m_date;
        QString m_time;
        QString m_file;
        quint32 m_line;
        QString m_msg;
    };
    Un template pour décoder/encoder les données :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <typename T>
    class QDataDecoder
    {
      public :
        virtual ~QDataDecoder() {}
     
        virtual T* decode(bool&, const QByteArray&) const = 0;
        virtual bool encode(const T*, QByteArray&) const = 0;
    };
    Une classe dérivée pour décoder/encoder les traces :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class QTraceDecoder : public QDataDecoder<QTraceData>
    {
      public :
        virtual QTraceData* decode(bool&, const QByteArray&) const;
        virtual bool encode(const QTraceData*, QByteArray&) const;
    };
    Un template pour représenter le modèle de données :
    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
    template <typename T>
    class QDataTableModel : public QAbstractTableModel
    {
      public :
        QDataTableModel(QObject* parent = 0)
          : QAbstractTableModel(parent) {}
     
        virtual int rowCount(const QModelIndex&) const
        {
          return m_data.size();
        }
     
        virtual bool insertRow(const T* data)
        {
          // insert data in m_data
        }
     
      protected :
        QList<const T*> m_data;
    };
    Une classe dérivée pour représenter le modèle de traces :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class QTraceTableModel : public QDataTableModel<QTraceData>
    {
      public :
        QTraceTableModel(QObject* parent = 0);
     
        virtual int columnCount(const QModelIndex&) const;
        virtual QVariant data(const QModelIndex&, int) const;
        QVariant headerData(int, Qt::Orientation, int) const;
        bool setData(const QModelIndex&, const QVariant&, int);
    };
    Cette partie pour l'instant me plaisait bien, mais voilà, elle entraîne un manque de généricité au niveau du module ou de la façade censée être le point d'entrée pour la communication avec les autres modules.

    J'ai, au départ, une classe pour la façade :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class QModelFacade : public Common::QFacade
    {
      public :
        QModelFacade() : Common::QFacade() {}
     
      public slots :
        virtual void interpretData(const QByteArray&) = 0; // appelée lors de la réception d'une trame réseau par le module Network
    };
    Ensuite, et c'est ici que ça se gâte , comme je n'arrivais pas à implémenter de façon générique la méthode interpretData avec les templates QDataDecoder et QDataTableModel (car je dois leur définir un type précis), j'ai créé, à contre coeur^^, une classe dérivée pour manipuler QTraceDecoder et QTraceTableModel :
    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
    class QTraceModelFacade : public QIModelFacade
    {
      public :
        QTraceModelFacade();
        ~QTraceModelFacade();
     
      public slots :
        virtual void interpretData(const QByteArray&);
     
      public :
        QTraceTableModelFacade* m_tableModelFacade;
     
      private :
        QTraceDecoder* m_decoder;
    };
    Au passage, je retrouve la même difficulté avec QTraceTableModelFacade, créée faute d'avoir trouvé le moyen de garder une classe générique QTableModelFacade.

    Ce manque de généricité entraîne, pour un changement de type de données, beaucoup de modifications à faire.. Encore devoir refaire une classe pour décoder ou une classe pour le modèle de ces nouvelles données d'accord, mais devoir recréer une classe façade ce n'est pas normal.

    Je ne maîtrise surement pas assez bien les templates pour contourner cette difficulté et c'est pourquoi je me permets de faire appel à vos connaissances .

    Merci

  2. #2
    Membre Expert

    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 : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Bonjour,

    Je n'ai pas tout compris à ton message, mais j'ai l'impression que tu t'emmele les pinceaux en jouant en même temps avec les templates et virtual.

    Typiquement, quel est l'intérêt de QDataDecoder ? Aucun, si ce n'est éventuellement l'illusion de définir proprement une interface et dans le pire des cas introduire des indirections à l'exécution.

    Tu as besoin dans le cas de ton application, ta bibliothèque de définir une notion de décodage et encodage depuis et vers un QByteArray ? Dans ce cas :
    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
     
    namespace N
    {
        template<class T>
        struct Encode;
     
        template<class T>
        struct Decode;
     
        template<>
        struct Encode<QTraceData>
        {
            QByteArray operator()(const QTraceData& data) const
            { /*implémentation*/ }
        };
     
        template<>
        struct Decode<QTraceData>
        {
            QTraceData operator()(const QByteArray& data) const
            { /*implémentation*/ }
        };
    }
    On aurait pu envisager de simple fonction template, mais c'est problématique si tu veux faire une spécialisation partielle par la suite. Utilisation :
    auto array_byte = Encode<QTraceData>()(a_trace );
    auto a_trace = Decode<QTraceData>()(array_byte);
    Après pour la suite de message, il y a à nouveau des héritages de classes templatées avec des virtuals dont je doute de la pertinence, et j'ai aussi un peu de mal à voir l'interaction de la suite avec ta première classe.

    De manière très général :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    template<class>
    struct A
    { virtual void foo(){}; };
     
    struct B {};
     
    struct C : A<B>
    { };
    N'est utile que si il y a d'autre classe que C qui hérité de A<B>, dans le cas contraire le caractère virtuel de foo est très probablement sans intérêt, et si foo était le seul contenu de A (foo et toutes ses consœurs virtuelles) alors c'est A qui perd aussi son intérêt..

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Par défaut
    Typiquement, quel est l'intérêt de QDataDecoder ? Aucun, si ce n'est éventuellement l'illusion de définir proprement une interface et dans le pire des cas introduire des indirections à l'exécution.
    Effectivement, c'est exactement ce que j'ai voulu faire avec QDataDecoder. Entre-temps, j'ai modifié mon implémentation pour arriver à quelque chose d'assez similaire à ce que tu proposes (même si dans ton cas, tu utilises plutôt des foncteurs et ça a l'avantage de ne pas avoir à définir une méthode decode et encode par défaut) :
    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
    namespace Model
    {
      class QDataDecoder
      {
        public :
          template <typename T>
          T* decode(const QByteArray&) const
          {
            return NULL;
          }
     
          template <typename T>
          bool encode(const T*, QByteArray&) const
          {
            return false;
          }
      };
     
      template<>
      QTraceData* QDataDecoder::decode<QTraceData>(const QByteArray&) const;
      template<>
      bool QDataDecoder::encode<QTraceData>(const QTraceData*, QByteArray&) const;
     
    } // Model
    Après pour la suite de message, il y a à nouveau des héritages de classes templatées avec des virtuals dont je doute de la pertinence, et j'ai aussi un peu de mal à voir l'interaction de la suite avec ta première classe.
    J'ai aussi défini une classe template pour représenter mon modèle QDataTableModel car cette fois-ci, j'avais besoin de le lier à mes données métiers via une liste de données passée en membre privé et comme la connaissance du type facilite beaucoup de choses dans le traitement ultérieur (pas de dynamic_cast à faire sur le type des données etc..), j'ai voulu "templatiser" la classe.

    Après, dés que la classe est templatisée, il est forcément moins évident d'avoir un module avec un QAbstractDecoder et un QAbstractModel et des méthodes virtuelles qui sont les mêmes pour toutes les classes filles car certaines méthodes ont besoin d'un type précis imposé par le template :s.

  4. #4
    Membre Expert

    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 : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Tu pourrais résumer où en est le problème dans son entièreté avec l'ensemble du code associé et l'objectif final ? Je suppose que c'est clair pour toi car tu es dedans, mais j'ai encore du mal à voir l'ensemble et le problème.

    Typiquement le code des différentes classes et un "main" avec une utilisation basique qui va dans le sens de ce que tu veux, même si ça ne compile pas, l'objectif est justement de voir où tu veux aller (et donc indirectement le problème).

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Par défaut
    Oups, désolé Flob90, je vais faire de mon mieux

    Objectif : développer une application de débogage qui, par réception de traces réseaux, peut les afficher dans un tableau et les manipuler.
    Après, je me suis dit "pourquoi pas faire quelque chose de générique qui permettrait d'afficher dans un tableau tout type d'information provenant du réseau ?".

    D'où mes modules :
    Network : pour recevoir les trames et les décoder
    Model : pour représenter le modèle du tableau
    View : pour représenter la vue liée au modèle

    Mon application devrait donc ressembler à quelque chose de la sorte :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void QAppli::init()
    {
      QGraphicFacade* graphicFacade = new QGraphicFacade();
      QModelFacade* modelFacade = new QModelFacade();
      QNetworkFacade* networkFacade = new QNetworkFacade();
     
      // Pour lier le modèle à la vue
      QAbstractView* view = graphicFacade->getView(idView);
      modelFacade->setModelToView(view);
     
      // Pour mettre à jour le modèle sur réception de trames par le module Network
      modelFacade->setModelToNetwork(networkFacade);
    }
    1) Le module Network aura donc pour charge de récupérer les trames, de les décoder, et de proposer les informations à qui le souhaite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void QNetworkFacade::QNetworkFacade()
      : m_config(new QNetworkConfig())
      , m_connection(new QNetworkConnection())
      , m_decoder(new QAbsDecoder()) // Pas possible d'instancier l'objet ici
    {
      createConnection();
    }
    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
    // Slot connecté au signal dataRead() de l'objet connection
    // Son rôle est de décoder le buffer et d'envoyer un signal avec les informations à qui veut s'y connecter
    bool QNetwork::interpretData(const QByteArray& buffer)
    {
      if (!m_decoder)
        return false;
     
      // QData est une classe abstraite héritée par tous les types de données qu'on souhaite récupérer
      QData* data = m_decoder->decode(buffer);
     
      if (m_model)
        m_model->insert(data); // ou emit dataDecoded(data) si connection signal/slot
     
      return true;
    }
    2) Le module Model va être associé à une vue et va recevoir les données du réseau.
    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
    void QModelFacade::QModelFacade()
      : m_config(new QModelConfig())
      , m_model(new QAbstractTableModel())
    {
    }
     
    void QModelFacade::setModelToView(QAbstractView* view)
    {
      view->setModel(m_model);
    }
     
    void QModelFacade::setModelToNetwork(QNetworkFacade* networkFacade)
    {
      networkFacade->setModel(m_model); // Ou une connection signal/slot
    }
    Pour le décodeur et le modèle tableau, j'ai donc utilisé des templates pour ensuite spécialiser le type voulu par héritage.
    Il faut bien de toute façon qu'à un moment ou à un autre, j'indique que ce qui m'intéresse pour le moment est de récupérer des traces, mais je ne sais pas quel endroit serait le plus judicieux pour le faire.

    Peut être mettre le decodeur dans une facade à part et le passer au module Network depuis la classe Application ?
    Et pour le modèle, où lui spécifier le type de données qu'il a à afficher ?

    J'espère avoir été plus clair

  6. #6
    Membre Expert

    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 : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Je suis pas un habitué du mvc et du facade, cependant si je comprends bien, la "pierre angulaire" de ce que tu présentes c'est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    QData* data = m_decoder->decode(buffer);
    Avant toute chose, ce que tu codes c'est une bibliothèque, destiné à un autre codeur, ou une application destiné à un utilisateur final ?

    Ce qui me gène dans cette ligne c'est que rien n'indique vers quel type le buffer doit être décodé. C'est information est-elle connue à la compilation ou uniquement à l'exécution ?
    • Dans le premier cas, elle devrait apparaître sous forme d'argument template dans cette ligne de code.
    • Dans le second cas, il doit y avoir un branchement conditionnel (direct ou indirect) qui se fait soit depuis une information fournie directement par l'application client soit depuis une information fournie dans le début du buffer (début qui devra donc toujours avoir le même encodage)

  7. #7
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Par défaut
    Avant toute chose, ce que tu codes c'est une bibliothèque, destiné à un autre codeur, ou une application destiné à un utilisateur final ?
    Ici, c'est une application pour un client final (moi-même ) pour déboguer éventuellement d'autres applications ou recevoir des données en tout genre. Mais bon, ce n'est pas une raison pour ne pas bien faire les choses .

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Ce qui me gène dans cette ligne c'est que rien n'indique vers quel type le buffer doit être décodé. C'est information est-elle connue à la compilation ou uniquement à l'exécution ?
    Oui effectivement, c'est un exemple qui ne compilera pas pour cette raison notamment. C'est pour ça que j'avais introduit plus haut (début du post) des templates pour définir le type de décodeur et le type de données dans le modèle.
    Maintenant, mon problème, c'est qu'avec des templates, ça va marcher, mais je vais me retrouver avec une façade qui sera elle-même templatisée pour définir un type précis au décodeur qu'elle comporte par exemple. Or, j'aurais voulu une unique façade pour chaque module.
    Si, à chaque nouveau type de données (chaque nouveau décodeur...) je dois recréer une façade (entre autre), c'est lourd. Ca ne respecte pas vraiment l'idée d'un programme réutilisable, modulable.

  8. #8
    Membre Expert

    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 : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    (Désolé pour la faute dans mon message précédent, c'est bien entendu "cette information" et pas "c'est information").

    Ma question sur la destination du programme n'était pas la pour te dire "c'est juste pour toi fait ça n'importe comment", mais pour essayer d'apporter des éléments de réponse adaptés. Développer une application final ou développer une bibliothèque pour un autre développeur, ça ne demande pas forcément la même approche dans tout les cas.

    Je pense avoir mieux saisi ton problème, mais tu ne réponds pas à ma question sur la connaissance de l'information sur le type de donné, compilation ou exécution ? La fin de ta réponse me fait cependant penser à une chose, l'application que tu développes est-elle destiné à ne décoder qu'un seul type de donné, ou alors plusieurs ? Je m'explique, il y a deux situations :
    • Ton application n'a besoin que de décoder un seul type de donné et la généricité t'apporte de quoi pouvoir changer le type de donné au fil de l'évolution du projet ou pour créer une seconde application pour un autre type de donné.
    • Ton application a besoin de décoder plusieurs type de donné et la généricité peut t'apporter de quoi faire évoluer l'ensemble des types possibles.


    Il y a un cas qui va être relativement simple, c'est celui où tu n'as besoin que de décoder un type de donné (donc celui-ci est nécessairement connu à la compilation, puisque unique). Dans ce cas, tu ne devrais pas avoir besoin de virtual et un simple "template<class T>" sur tout ce qui existe déjà peut convenir (ou presque).

    Dans le second cas, plusieurs type de donné possible et déterminé à l'exécution, on se retrouve dans une situation plus complexe :
    • Tu dois avoir un élément qui extrait une "en-tête" du buffer et qui représente le type réel de la donné à décoder.
    • Un élément utilise cette en-tête pour décoder le corps du buffer.
    • Tu dois stoker la donné en faisant abstraction de son type, typiquement un type-erasure. C'est typiquement ce genre de chose qui peut impliquer l'utilisation de classes tempate et de fonctions virtuelles.
    • Tu dois manipuler l'ensemble des donnés, soit au travers d'une interface commune si elle existe et fait sens, soit au travers d'un système de visitation.


    Le second cas est nettement plus complexe, et aux travers de tes messages j'ai quand même le sentiment qu'on est plutôt dans la première situation, est-ce le cas ?

  9. #9
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Par défaut
    (Désolé pour la faute dans mon message précédent, c'est bien entendu "cette information" et pas "c'est information").
    Pas de problèmes pour la compréhension de ta phrase ;-)

    Ma question sur la destination du programme n'était pas la pour te dire "c'est juste pour toi fait ça n'importe comment", mais pour essayer d'apporter des éléments de réponse adaptés. Développer une application final ou développer une bibliothèque pour un autre développeur, ça ne demande pas forcément la même approche dans tout les cas.
    Tu as raison tout à fait raison! Je disais plus ça sur le ton de l'amusement en fait .

    Pardon de ne pas avoir clairement répondu à ta question.
    Le second cas est nettement plus complexe, et aux travers de tes messages j'ai quand même le sentiment qu'on est plutôt dans la première situation, est-ce le cas ?
    Effectivement, dans mon cas, je veux récupérer des traces mais laisser la possibilité avec mon architecture pour "accueillir" d'autres types de données à récupérer sur le réseau et à afficher.
    Je ne souhaite pas ajouter une complexité au niveau de l'envoi des données sur le réseau, à savoir ici, une en-tête pour définir le type de données.
    Je pars de l'idée que celui qui exécute l'application de débogage, ici moi, à connaissance du type de données qu'il veut afficher.

    Dans ce cas, tu ne devrais pas avoir besoin de virtual et un simple "template<class T>" sur tout ce qui existe déjà peut convenir (ou presque).
    Donc dans la façade Network par exemple, tu donnerais explicitement le type de données pour les foncteurs de décodage/encodage ?
    A chaque fois que tu changerais de type de données, tu viendrais modifier la façade directement en spécialisant le nouveau type ?
    Ne serait-il pas mieux de déplacer cette spécialisation au niveau de la méthode init de QAppli ?

    Merci à toi :-)

  10. #10
    Membre Expert

    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 : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Avant propos: Je ne sais pas si tu es en C++11 ou pas, dans le doute je reste en 03, mais certains points sont simplifié en C++11.

    Basiquement oui, mais on peut affiner un peu la réflexion.

    La première chose est d'identifier clairement l'élément qui a besoin de varier, ici il s'agit très clairement de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    QData* data = m_decoder->decode(buffer);
    Et plus exactement de l'appel à "decode", donc fondamentalement une fonction, et du type QData. Comment ces deux choses peuvent varier :
    • L'appel en passant par un foncteur.
    • Le type en passant par un paramètre template.

    Cependant il est assez évident que ces deux éléments ne sont pas orthogonaux (le retour de l'appel impose le type). Je vois deux pistes :
    • La première est de déduire le type en utilisant directement un foncteur que l'on passerait à la construction. Ça demande de passer par une fonction de construction externe pour être viable (je m'occupe pas de tout ce qui est pointeur, allocation dynamique, accessibilité et autre dans les codes d'exemple, c'est juste les grandes lignes) :
      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
       
      #include<functionnal>
       
      template<class Data>
      struct Network
      {
          std::function<Data(const ByteArray&)> decode;
          Model model;
       
          template<class F>
          Network(F f) : decode(f) {}
       
          void interpret(const ByteArray& buffer)
          { model.insert(decode(buffer)); }
      };
       
      template<class F>
      Network<typename result_of<F(const ByteArray)>::type> make_network(F f)
      { return Network<typename result_of<F>::type>(f); }
      Je te laisse googler pour avoir des infos sur result_of. Ce code est épuré, mais il fait quand même apparaître quelque chose d'intéressant, l'expression dans le code de interpret a lui toujours le même type de retour.
    • C'est l'approche de la seconde solution, à la place de stocker decode qui nous embête un peu avec son type de retour qui change, on pourrait directement stocker insert(decode). Je le fais avec une lambda par soucis de simplicité, mais au besoin on peut le faire sans :
      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
       
      struct Network
      {
          Model model;
          std::function<void(Model&, const ByteArray&)> feed;
       
          template<class F>
          Network(F f) : 
              feed(
                  [f](Model& m, const ByteArray& a)
                  { m.insert(f(a)); }
              )
          {}
       
          void interpret(const ByteArray& buffer)
          { feed(model,buffer); }
      };
       
      //Utilisation, avec la classe Decode que j'ai exposé au début de la discussion (premier message) :
      Network n(Decode<TraceData>())
      Avec ce code on conserve une flexibilité au niveau du type de donné et du modèle utilisé, il est possible de créer des fonctions pour changer de modèle ou de type de donné après la création de la classe Network (absente du code). Pour la lambda, tu peux la "simuler" avec une classe template proche de :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
       
      template<class F>
      struct feed_func
      {
          F f;
          feed(F f) : f(f) {}
          void operator()(Model& m, const ByteArray& a)
          { m.insert(f(a)); }
      };
       
      //Utilisation à la place de la lambda
      feed(feed_func<F>(f))


    C'est probablement par parfait, il reste surement des choses à voir et améliorer, mais ça me semble une piste viable. Note que je n'ai pas testé de code, il peut rester des coquilles.

    Edit: Dans les choses à voir et à améliorer, il y a entre autre le fait que le modèle et la fonction de décodage utilisée sont liées, ce qui fait que la flexibilité que j'annonce est assez illusoire en l'état actuel.

Discussions similaires

  1. Problème avec les templates (patrons)
    Par bounadalvidal dans le forum Débuter
    Réponses: 3
    Dernier message: 09/04/2011, 07h14
  2. Problème avec les templates.
    Par mondaying dans le forum C++
    Réponses: 5
    Dernier message: 08/03/2011, 19h03
  3. [Xtext] Problème avec les templates pour les mots clé
    Par P1t0u dans le forum Eclipse Platform
    Réponses: 0
    Dernier message: 10/06/2010, 15h53
  4. Problème avec les templates de class
    Par _SamSoft_ dans le forum C++
    Réponses: 8
    Dernier message: 21/08/2008, 10h30
  5. Problème avec les templates
    Par F-fisher dans le forum C++
    Réponses: 7
    Dernier message: 28/06/2008, 16h04

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