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 :

Variadic template policy et polymorphisme


Sujet :

C++

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2014
    Messages : 8
    Points : 9
    Points
    9
    Par défaut Variadic template policy et polymorphisme
    Bonjour,

    J'ai un problème de conception et je sèche un peu. Peut être avez vous une petite idée ou piste pour m'aider.
    Je cherche une solution élégante pour cacher l'implémentation réseau que j'utilise dans mon logiciel.
    J'utilise des variadic template et une politique qui me permet de choisir de quelle façon je vais sérialiser mes objets et de ne pas m’inquiéter du nombre de membre.

    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
     
                    template<typename SerializePolicy>
    		class NetworkSerializer
    		{
    		public:
     
     
    			template<typename T, typename... Arguments>
    			void serialize(T pValue)
    			{
    				static_cast<SerializePolicy*>(this)->serializeStrategy(pValue);
    			}
     
    			template<typename T, typename... Arguments>
    			void serialize(T pValue, Arguments... pArgs)
    			{
    				static_cast<SerializePolicy*>(this)->serializeStrategy(pValue);
    				serialize(pArgs...);
    			}
     
     
    			template<INetworkObservable*, typename... Arguments>
    			void serialize(INetworkObservable* pNetObs)
    			{
    				static_cast<SerializePolicy*>(this)->serializeStrategy(pNetObs);
    			}
     
     
    			template<INetworkObservable*, typename... Arguments>
    			void serialize(INetworkObservable* pNetObs, Arguments... pArgs)
    			{
    				static_cast<SerializePolicy*>(this)->serializeStrategy(pNetObs);
    				serialize(pArgs...);
    			}
     
     
    		};

    Je suis obligé d'écrire:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    NetworkSerializer<MyNetworkSerializationPolicy> ns;
    Cependant je veux cacher la politique que j'utilise, de sorte que mon logiciel puisse appeler le NetworkSerializer sans en connaitre la politique.
    Je ne pense pas pouvoir utiliser des type erasure puisque mes fonctions sont templatés et que j'utilise des traits pour faire de la spécialisation partielle dans l'implémentation des policy, ou alors je suis passé à coté de quelque chose.

    Une aide serait la bienvenue.

    Merci.

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Salut,

    pourquoi pas tout simplement typedef NetworkSerializer<MyNetworkSerializationPolicy> MyNetworkSerializer; pour pouvoir faire MyNetworkSerializer ns; ?
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2014
    Messages : 8
    Points : 9
    Points
    9
    Par défaut
    Bonjour,

    Ca pourrait être une solution mais je perdrai pas mal d'avantage: Si je change d'implémentation pour le réseau je dois changer le typedef. De plus si le choix de mon implémentation est fait dans un fichier de config chargé au démarrage et que je choisis entre plusieurs NetworkSerializer<XXXXX> je ne peux pas utiliser cette solution.

  4. #4
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,
    Citation Envoyé par D-j-O Voir le message
    Bonjour,

    Ca pourrait être une solution mais je perdrai pas mal d'avantage: Si je change d'implémentation pour le réseau je dois changer le typedef. De plus si le choix de mon implémentation est fait dans un fichier de config chargé au démarrage et que je choisis entre plusieurs NetworkSerializer<XXXXX> je ne peux pas utiliser cette solution.
    Là, je crois qu'il y a confusion...

    Un fichier config ne pourra être utilisé qu'à l'exécution, le typedef (ou, de manière générale, l'instanciation d'une classe template) s'effectue à la compilation. Si tu as plusieurs politiques qui t'intéressent et que tu souhaites pouvoir sélectionner l'une de ces politiques à l'exécution, il faudra que tu te soies arrangé pour que chaque politique qui t'intéresse ait été spécifiquement instanciée. La solution "classique" pour y arriver consiste à créer ce que l'on appelle une instanciation explicite.

    Il ne faut donc pas confondre les différentes possibilités :

    Soit tu veux décider toi-même une bonne fois pour toutes quelle est la meilleure politique à appliquer (quitte à changer dans deux versions), et, à ce moment là, le typedef semble être la solution la plus adaptée : si tu décide de choisir une autre politique, tu adaptes ton typedef, tu recompiles, et le tour est joué.

    Soit tu veux pouvoir sélectionner à l'exécution la politique la plus intéressante. A ce moment, tu devras veiller à ce que toutes les politiques envisagées soient instanciées, et il y a intérêt à veiller d'avoir classe de base fournissant les différents services attendus, de manière à pouvoir avoir un pointeur sur cette classe de base que tu feras pointer sur la politique adéquate par rapport à la configuration (bre, de l'orienté objet pur et simple, bien que les classes dérivées puissent être des classes templates )

    Commence donc par faire le point sur ce qui est sur et certain, le reste, tu t'en inquiéteras "en temps utiles"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Futur Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2014
    Messages : 8
    Points : 9
    Points
    9
    Par défaut
    Effectivement mon exemple de fichier de config n'est pas pertinent ici.
    Ce qui m'embête c'est de devoir montrer l'implémentation dans du code généric.

    Par exemple dans mon manager de réseau je vais devoir écrire ça en suivant vos conseils

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    using NetworkSerializerImpl = NetworkSerializer<MySerializationPolicy>;
    A priori je ne veux pas pas que mon implémentation soit visible ici. Mais je n'ai pas vraiment le choix et ça reste plutôt élégant tout de même.

    ... avoir un pointeur sur cette classe de base que tu feras pointer sur la politique adéquate par rapport à la configuration (bre, de l'orienté objet pur et simple, bien que les classes dérivées puissent être des classes templates
    Ton dernier point m'intéresse mais je ne comprends pas ce que tu veux dire. Je ne peux pas avoir une classe de base avec des fonctions virtuelles si celles ci sont templatées.

  6. #6
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Salut !

    En effet, tu es ici face à un choix : si tu veux appeler des fonctions membres templates (variadiques qui plus est), tu es obligé de connaître le type réel. Si tu veux cacher l'implémentation, tu dois cacher le type réel.

    Maintenant, un compromis exite : tu peux utiliser une classe template (dont le type réel sera connu), et qui elle appelle l'interface d'un serialiseur dont elle ne connait pas le type réel, pour peu que tout ce qui est appelé n'est pas template. Ainsi, tu gardes tes templates dans le code appelant et tu peux changer le sérialiseur à l'exécution si cela te plait. Ca s'utiliserais comme ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int main() {
      NetworkSerializer ns_dumper;
     
      ns_dumper.SetSerializerImpl(new SerializerCSV);
      std::cout << ns_dumper.Serialize(1, 2.3,"Roger",4) << "\n";
     
      ns_dumper.SetSerializerImpl(new SerializerJSON);
      std::cout << ns_dumper.Serialize(2.3, 1,"Roger",4,"Marcel") << "\n";
     
      return 0;
    }
    Ca affiche ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    1;2.3;Roger;4
    [2.3,1,"Roger",4,"Marcel"]
    L'implémentation :

    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
    #include <string>
    #include <sstream>
    #include <ostream>
    #include <iostream>
    #include <memory>
     
    struct Serializer {
      virtual ~Serializer() {}
     
      virtual void StartRecord() = 0;
      virtual void EndRecord() = 0;
      virtual std::string GetRecord() const = 0;
     
      virtual void Push(int value) = 0;
      virtual void Push(double value) = 0;
      virtual void Push(std::string const& value) = 0;
      // etc avec tous les types possibles
    };
     
    class SerializerCSV : public Serializer {
      std::ostringstream record_stream_;
      std::string result_;
      bool has_data_ = false;
     
      void PushSep() {
        if (has_data_) record_stream_ << ';';
        has_data_ = true;
      }
     
     public:
      void StartRecord() override {
        result_ = "";
        record_stream_.str("");
        has_data_ = false;
      }
     
      void EndRecord() override { result_ = record_stream_.str(); }
      std::string GetRecord() const override { return result_; }
      void Push(int value) override { PushSep(); record_stream_ << value; } 
      void Push(double value) override { PushSep(); record_stream_ << value; }
      void Push(std::string const& value) override { PushSep(); record_stream_ << value; }
    };
     
    class SerializerJSON : public Serializer {
      std::ostringstream record_stream_;
      std::string result_;
      bool has_data_ = false;
     
      void PushSep() {
        if (has_data_) record_stream_ << ',';
        has_data_ = true;
      }
     
     public:
      void StartRecord() override {
        result_ = "";
        record_stream_.str("");
        record_stream_ << "[";
        has_data_ = false;
      }
     
      void EndRecord() override { 
        record_stream_ << ']';
        result_ = record_stream_.str(); 
      }
      std::string GetRecord() const override { return result_; }
      void Push(int value) override { PushSep(); record_stream_ << value; } 
      void Push(double value) override { PushSep(); record_stream_ << value; }
      void Push(std::string const& value) override { PushSep(); record_stream_ << '"' << value << '"'; }
    };
     
    class NetworkSerializer {
      std::unique_ptr<Serializer> serializer_impl_;
     
      void Push() {}
      template <typename Arg, typename ... Args> void Push(Arg value, Args ... values) {
        serializer_impl_->Push(value);
        Push(values ...);
      }
     
     public:
      void SetSerializerImpl(std::unique_ptr<Serializer>&& serializer) { serializer_impl_ = std::move(serializer); }
      void SetSerializerImpl(Serializer* serializer) { SetSerializerImpl(std::unique_ptr<Serializer>(serializer)); }
     
      template <typename ... Args> std::string Serialize(Args ... values) {
        serializer_impl_->StartRecord();
        Push(values...);
        serializer_impl_->EndRecord();
        return serializer_impl_->GetRecord();
      }
    };
    C'est assez simple et ça s'approche de ton objectif, je pense, à arranger à ta sauce.
    Find me on github

  7. #7
    Futur Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2014
    Messages : 8
    Points : 9
    Points
    9
    Par défaut
    Merci!!!

    Ta solution est vraiment pas mal. Je vais regarder comment je peux et si c'est pertinent d'intégrer ça.

    Merci à vous tous.

Discussions similaires

  1. Réponses: 19
    Dernier message: 23/12/2009, 19h22
  2. Template, héritage et polymorphisme
    Par PtitFrancesco dans le forum Langage
    Réponses: 5
    Dernier message: 08/09/2009, 21h35
  3. [C++0x] Variadic template : un constructeur par type
    Par Florian Goo dans le forum Langage
    Réponses: 2
    Dernier message: 08/04/2009, 18h33
  4. Réponses: 2
    Dernier message: 10/01/2009, 13h38

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