Partage
  • Partager sur Facebook
  • Partager sur Twitter

factory generique ?

construction avec variadic template

    20 septembre 2017 à 16:24:55

    Bonjour ,

    J'aimerais pouvoir me faire un 'factory generique' grace au templates et variadic arguments.

    mon probleme viens au moment d'apeller le constructeur .. comment puis-je 'décompresser' la liste d'arguments afin de la transmettre au constructeur. Sinon , comment pourrais-je arriver a mon but ?

    Voici ce que je souhaiterais faire ..

    class Base{
        public:
        enum btype{X,Y};
        Base(btype t):type_{t}{};
        public: btype type_;
    };
    
    class A : public Base{
        public:std::string value_;int other_;
        public:A(std::string c, int o):Base{Base::btype::X},value_{c},other_{o}{}
    };
    
    class B : public Base{
         public:std::string value_;char other_;
         public:B(std::string c, char o):Base{Base::btype::Y},value_{c},other_{o}{}
    };
    
    class foo{
        public:
        template <class T,typename... Args>
        static T make(Args... args){
            return T(/* args .. */); /** ICI **/   
        }
    };
    
    int main()
    {
        A a = foo:make<A>("class A",0);
        B b = foo::make<B>("class B",'B');
    }

    Comment pourrais-je arriver a un comportement semblable a ce que je tente de faire dans mon main ?

    Merci d'avance pour vos réponses.

    EDIT :: Mais quelle question ridicule ... il fallais simplement enlever le commentaire ( je ne croyais pas en fait que args... serais déployé en liste d'argument pour le constructeur ...

    -
    Edité par CrevetteMagique 20 septembre 2017 à 16:33:37

    • Partager sur Facebook
    • Partager sur Twitter
      20 septembre 2017 à 17:03:51

      Utilise std::forward + l'opérateur "..." :)

      Et pourquoi faire appel au constructeur de ta class via les parenthèses plutôt que les accolades ?

      • Partager sur Facebook
      • Partager sur Twitter
      La flemme vaincra.
        20 septembre 2017 à 17:05:43

        zeFresk a écrit:

        Utilise std::forward + l'opérateur "..." :)

        Et pourquoi faire appel au constructeur de ta class via les parenthèses plutôt que les accolades ?


        Pour le constructeur j'ai ecris ce code sur coliru tres vite afin de demontrer mn besoin , j'utilise toujours la liste D'initialisation en fait :) Merci de ta reponse je vais voir pour forward.

        edit :: est-ce bien la bonne syntaxe pour le forward ?

        return T{std::forward<Args>(args)...};



        -
        Edité par CrevetteMagique 20 septembre 2017 à 17:11:22

        • Partager sur Facebook
        • Partager sur Twitter
          23 septembre 2017 à 10:05:27

          Oui c'est la bonne syntaxe.
          • Partager sur Facebook
          • Partager sur Twitter
          Mettre à jour le MinGW Gcc sur Code::Blocks. Du code qui n'existe pas ne contient pas de bug
            26 septembre 2017 à 17:23:08

            Salut, voila l'idée.

            template<typename T, typename... Args>
            std::unique_ptr<T> factory(Args... a){
               return std::make_unique<T>(std::forward<Args>(a)... );
            }


            Pour ton code:

            #include <memory>
            #include <stdexcept>
            
            struct Base{
              virtual ~Base(){}
            };
            
            struct A:Base{
              A(int,std::string){}
            };
            
            struct B:Base{
              B(double){}
            };
            
            
            template<typename ... Args>
            std::unique_ptr<Base*> factory(const std::string &what, Args... a){
                if(what=="A"){return std::make_unique<A>(std::forward<Args>(a)...);}
                if(what=="B"){return std::make_unique<B>(std::forward<Args>(a)...);}
                throw std::runtime_error("Invalid type");
            }


            Les trucs importants:
            - Base doit avoir un destructeur virtuel
            - Base contient, dans le cas pratique, d'autres fonctions virtuelles, parce que sinon, ta factory sers a rien, parce que les types construits sont hétérogènes.
            - ta factory renvoie probablement un pointeur (ou mieux, un std::unique_ptr) vers ta classe de base. Si ce n'est pas le cas, t'as pas besoin d'une factorty et y'a un truc fumeux dans ta conception.
            - pour passer les arguments, c'est std::forward<Args>(a)...


            -
            Edité par ledemonboiteux 26 septembre 2017 à 17:29:40

            • Partager sur Facebook
            • Partager sur Twitter
              26 septembre 2017 à 17:58:51

              ledemonboiteux a écrit:

              Salut, voila l'idée.

              template<typename T, typename... Args>
              std::unique_ptr<T> factory(Args... a){
                 return std::make_unique<T>(std::forward<Args>(a)... );
              }


              Pour ton code:

              #include <memory>
              #include <stdexcept>
              
              struct Base{
                virtual ~Base(){}
              };
              
              struct A:Base{
                A(int,std::string){}
              };
              
              struct B:Base{
                B(double){}
              };
              
              
              template<typename ... Args>
              std::unique_ptr<Base*> factory(const std::string &what, Args... a){
                  if(what=="A"){return std::make_unique<A>(std::forward<Args>(a)...);}
                  if(what=="B"){return std::make_unique<B>(std::forward<Args>(a)...);}
                  throw std::runtime_error("Invalid type");
              }


              Les trucs importants:
              - Base doit avoir un destructeur virtuel
              - Base contient, dans le cas pratique, d'autres fonctions virtuelles, parce que sinon, ta factory sers a rien, parce que les types construits sont hétérogènes.
              - ta factory renvoie probablement un pointeur (ou mieux, un std::unique_ptr) vers ta classe de base. Si ce n'est pas le cas, t'as pas besoin d'une factorty et y'a un truc fumeux dans ta conception.
              - pour passer les arguments, c'est std::forward<Args>(a)...


              -
              Edité par ledemonboiteux 25 minutes ago

              Pouruqoi avoir mis un string 'what' dans ton factory ? Aussi bien utiliser le template , et si c'est pour 'verifier le type' il ya toujours un static_assert( std::is_base_of<> ... ).

              class foo{
                  public:
                  template <class T,typename... Args>
                  static T make(Args... args){
                      static_assert(std::is_base_of<Base,T>::value, "Invalid Type");
                      return T(std::forward<Args>(args)...); 
                  }
              };

              C'est bien un std::unique_ptr renvoyé par ma factory.

              • Partager sur Facebook
              • Partager sur Twitter
                26 septembre 2017 à 18:07:32

                Bah tout le but de la factory c'est de découpler pour que le code appelant n'a pas à connaitre les sous types de Base.

                Avec des templates comme ça, la factory n'apporte pas grand chose...

                • Partager sur Facebook
                • Partager sur Twitter
                  26 septembre 2017 à 18:15:44

                  epso a écrit:

                  Bah tout le but de la factory c'est de découpler pour que le code appelant n'a pas à connaitre les sous types de Base.

                  Avec des templates comme ça, la factory n'apporte pas grand chose...


                  Alors je ne vois tout simplement pas la difference entre connaitre le type de base et connaitre sa représentation en string ? Que ca soit le string ou le type , il faut connaitre 'le type' que nous voulons non ?
                  • Partager sur Facebook
                  • Partager sur Twitter
                    26 septembre 2017 à 18:56:29

                    Non, typiquement la string elle peut venir directement d'un fichier sans qu'on ait à connaitre sa valeur précise.

                    Et il y a une autre grosse différence, pour pouvoir instancier un type, il faut connaitre sa définition via un include, et ça, c'est lourd.

                    Mais c'est aussi pour ça que généralement les factory utilisent pour tous les types les même arguments (après, ça reste vague, ça peut être un flux, un dictionnaire, un fichier xml ou json, ...), sans ça il faut de toute façon changer le code appelant selon le type, et encore une fois, la factory ne sert plus à rien.

                    L'idée typique d'une factory est juste permettre d'avoir un bout de code qui peut charger une collection d'objets héritant tous de la même classe mère, par exemple à partir d'un fichier ou d'un flux utilisateur ou réseau, et ce sans avoir à modifier ce code à chaque fois que tu rajoute une nouvelle classe (violation de l'OCP).

                    • Partager sur Facebook
                    • Partager sur Twitter
                      26 septembre 2017 à 19:02:54

                      epso a écrit:

                      Non, typiquement la string elle peut venir directement d'un fichier sans qu'on ait à connaitre sa valeur précise.

                      Et il y a une autre grosse différence, pour pouvoir instancier un type, il faut connaitre sa définition via un include, et ça, c'est lourd.

                      Mais c'est aussi pour ça que généralement les factory utilisent pour tous les types les même arguments (après, ça reste vague, ça peut être un flux, un dictionnaire, un fichier xml ou json, ...), sans ça il faut de toute façon changer le code appelant selon le type, et encore une fois, la factory ne sert plus à rien.

                      L'idée typique d'une factory est juste permettre d'avoir un bout de code qui peut charger une collection d'objets héritant tous de la même classe mère, par exemple à partir d'un fichier ou d'un flux utilisateur ou réseau, et ce sans avoir à modifier ce code à chaque fois que tu rajoute une nouvelle classe (violation de l'OCP).


                      Je vois mieu maintenant l'utilisation du string , merci a toi.
                      • Partager sur Facebook
                      • Partager sur Twitter
                        2 octobre 2017 à 10:04:11

                        Si tu passes le code de l'objet a construire sous la forme d'un paramètre template, c'est que tu le connais a la compilation. Dans ce cas, autant ne pas passer par une factory et construire l'objet directement avec son constructuer ou std::make_unique si on veut un pointeur.


                        Si tu as besoin d'une factorty, c'est que le type de l'objet à construire n'est pas connu a l'exécution. Il dépend d'un if, est lu dans un fichier, ou que sais-je. Dans ce cas, tudois pouvoir appeler ta factory et décider dynamiquement dedans du type a construire (c.f. cpost de Zérotisme au dessus)

                        @Zérotisme : sans rapport, mais j'aime bien ce psedo :)

                        • Partager sur Facebook
                        • Partager sur Twitter
                          3 octobre 2017 à 14:31:26

                          ledemonboiteux a écrit:

                          Si tu passes le code de l'objet a construire sous la forme d'un paramètre template, c'est que tu le connais a la compilation. Dans ce cas, autant ne pas passer par une factory et construire l'objet directement avec son constructuer ou std::make_unique si on veut un pointeur.


                          Si tu as besoin d'une factorty, c'est que le type de l'objet à construire n'est pas connu a l'exécution. Il dépend d'un if, est lu dans un fichier, ou que sais-je. Dans ce cas, tudois pouvoir appeler ta factory et décider dynamiquement dedans du type a construire (c.f. cpost de Zérotisme au dessus)

                          @Zérotisme : sans rapport, mais j'aime bien ce psedo :)


                          Merci , totalement inspiré de l'anciens nom de ce site ^^
                          • Partager sur Facebook
                          • Partager sur Twitter

                          factory generique ?

                          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                          × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                          • Editeur
                          • Markdown