Partage
  • Partager sur Facebook
  • Partager sur Twitter

Foncteurs C++ , pointeur sur fonction

C++/Qt

    18 août 2018 à 12:13:38

    a) De plus, par rapport au transtypage, j'ai appris avec le tutoriel que void* est un fourre-tout pour tous les types.

    b) Est-ce que , tout comme tu l'as fait pour passer d'un env* vers un void*, aurait-on le droit spécialiser d'un void* vers un int (ou bien ça devrait forcément être un int*) ?

    a) Bin, si un cours dit ça, il faut juste le jeter. 

    void*, c'est pas un fourre tout. Une variable de type void*  sert à contenir une adresse, mais l'adresse d'une chose dont le compilateur ignore le type. C'est donc un pointeur, mais on ne peut pas le déréférencer (lui appliquer * ou ->).

    Le type void* a été introduit en C pour essayer d'arranger le merdier originel, où on utilisait char* comme pointeur générique, en confusionnant allègrement char avec octet, et pointeur avec entier au passage.


    Il est assez grand pour contenir tous les types de pointeurs dispos sur l'architecture. Autrefois (70's), les pointeurs étaient de la même taille, qui était celle d'un entier. Maintenant on ne suppose plus cela, surtout que ce n'est pas vrai du tout. Donc on ne peut pas mettre un entier dans un pointeur ou le contraire (réponse à b), au moins sans danger pour la portabilité.

    Dans l'embarqué, on peut avoir des pointeurs de taille différente selon qu'il s'agit de pointer en mémoire (qui est petite), ou en mémoire flash. Exemple sur arduino at mega, RAM de 8k (il suffit de 13 bits) et flash 256K (18 bits). Dans un cas, il faudra 2 octets, dans l'autre 3.

    En C, les pointeurs sur void sont compatibles (pas besoin de cast) avec les pointeurs sur autre chose. Exemple

        int i = 1234;
        
        int  * p_int;
        void * p_void;
        
        p_int  = & i;
        p_void = p_int;               // pas de problème
        p_int  = p_void;              // pareil
        
        printf("%d\n", *p_int);

    On reconnait facilement les cours de C qui ont été écrits à l'époque des dinosaures quand on y voit :

          struct Truc *ptr =  (struct Truc *) malloc(sizeof(struct Truc));   -- le cast est inutile

    En C++ c'est différent. static_cast is the way to go.

    -
    Edité par michelbillaud 18 août 2018 à 12:50:39

    • Partager sur Facebook
    • Partager sur Twitter
      18 août 2018 à 12:37:31

      @MicheBillaud

      ton message n'est passé correctement. Il y a une partie du mien qui est recopiée.

      -
      Edité par pseudo-simple 18 août 2018 à 12:38:30

      • Partager sur Facebook
      • Partager sur Twitter
        18 août 2018 à 12:49:00

        YES, man a écrit:

        @MicheBillaud

        ton message n'est passé correctement. Il y a une partie du mien qui est recopiée.

        -
        Edité par YES, man il y a 9 minutes


        J'ai la facheuse manie de commencer un message et de le publier pour le sauver bien avant qu'il soit fini. Et de trouver des choses à y rajouter. Il apparait donc incomplet. Désolé.

        -
        Edité par michelbillaud 18 août 2018 à 12:52:12

        • Partager sur Facebook
        • Partager sur Twitter
          19 août 2018 à 12:31:43

          Bonjour,

          je suis arrivé sur l'utilisation de generate() avec algorithm.

          Dans cours de mathieu, il y a l'exemple suivant :

          #include <algorithm>
          
          #include <vector>
          
          using namespace std;
          
          
          //Définition de Remplir…
          
          
          int main()
          
          { 
          
              vector<int> tab(100,0); //Un tableau de 100 cases valant toutes 0
          
          
              Remplir f(0);       
          
          
              generate(tab.begin(), tab.end(), f);
          
              //On applique f à tout ce qui se trouve entre begin() et end()
          
              
          
              return 0;
          
          }



          Je me suis alors demandé quel lien on peut faire avec l'exemple donné par gbdivers il y a quelques jours qui fut le suivant :

          #include <vector>
          #include <algorithm>
          #include <iostream>
           
          void foo(int i) {
              std::cout << i << std::endl;
          }
            
          int main()
          {
              std::vector<int> v = {3, 4, 2, 8, 15, 267};
               
              std::for_each(std::begin(v), std::end(v), foo);
            
              std::for_each(std::begin(v), std::end(v), [](int i){ std::cout << i << std::endl; });
          }

          Est-ce generate() utilise for_each()  ?

          Merci

          • Partager sur Facebook
          • Partager sur Twitter
            19 août 2018 à 12:44:06

            YES, man a écrit:

            Est-ce generate() utilise for_each()  ?

            Peux tu preciser ta question ?

            • Partager sur Facebook
            • Partager sur Twitter
              19 août 2018 à 13:18:06

              Je veux dire que j'ai l'impression qu'il y a un lien entre for_each et generate.
              • Partager sur Facebook
              • Partager sur Twitter
                19 août 2018 à 13:31:19

                Ben oui... for_each applique une fonction sur chaque element, generate applique une fonction pour modifier chaque element. Forcement que c'est un peu lié, ces fonctions font des choses assez proches. Comme toutes les fonctions qui parcourent les tableaux. Je ne suis pas sur de comprendre a quel type de lien tu t'attends.

                • Partager sur Facebook
                • Partager sur Twitter
                  19 août 2018 à 13:59:19

                  Merci, tu as vu la subtilité. D'accord, donc for_each applique une fonction sur chaque élément (sans le modifier) tandis que generate() a la capacité de modifier les éléments du conteneur.

                  EDIT : voici de morceau de code que je viens de tester :

                  #include <iostream>
                  #include <vector>
                  #include <algorithm>
                  
                  using namespace std;
                  
                  
                  class Remplir{
                  public:
                      Remplir(int i):m_valeur(i)
                      {}
                  
                      int operator()(){
                      ++m_valeur;
                      return m_valeur;
                      }
                  
                  private:
                      int m_valeur;//{-1};
                  };
                  
                  int main()
                  {
                  
                  
                  
                      Remplir foncteur(0);
                      vector<int> v(100,0);
                  
                      generate(v.begin(),v.end(),foncteur);
                      generate(v.begin(),v.begin()+10,foncteur);

                  En principe, puisque mon foncteur fait office de mémoire, j'aurais pensé qu'après le premier generate(), la valeur enregistrée est 100.

                  Pourtant lorsque j'applique le deuxième generate() et que j'affiche, il semblerait que ça m'affiche de 1 à 100
                  Et pas de 101 à 110 pour les 10 premiers éléments.

                  comportement auquel je ne m'attendais pas

                  -
                  Edité par pseudo-simple 19 août 2018 à 15:22:51

                  • Partager sur Facebook
                  • Partager sur Twitter
                    19 août 2018 à 16:26:04

                    D’après la doc, generate est déclarée de la sorte :

                    template&lt; class ForwardIt, class Generator &gt;
                    void generate( ForwardIt first, ForwardIt last, Generator g );
                    

                    Si je ne dit pas de bêtises, dans ton cas Generator sera remplace (si quelqu'un peut me rappeler le terme correct pour le choix du type des templates, je suis preneur) par Remplir, effectuant donc une copie. Du coup le premier generate travaille sur une copie de ton foncteur (le 2em generate aussi) ce qui explique pourquoi tes valeurs se reset a 0.

                    • Partager sur Facebook
                    • Partager sur Twitter
                      19 août 2018 à 16:43:40

                      Elried a écrit:

                      si quelqu'un peut me rappeler le terme correct pour le choix du type des templates, je suis preneur

                      instanciation. (Une "function template" est instanciée en "template function" ;) )

                      • Partager sur Facebook
                      • Partager sur Twitter
                        19 août 2018 à 16:43:55

                        Ce qui se passe, c'est que le générateur est transmis par valeur à std::generate.

                        Si bien que, après le premier appel, si tu vérifie la valeur de m_valeur dans foncteur, tu remarquera qu'elle est toujours égale à ... 0, et que, du coups, il est "normal" que le deuxième appel à générate utilise les valeurs 0, 1, 2 ... 10 :D

                        (d'ailleurs, cela n'a pas vraiment de sens de mettre m_valeur en accessibilité privée ici, ni même d'utiliser le mot clé class : pour les foncteurs, on utilise généralement struct, et on met généralement tout en public :D ... mais bon...)

                        Tu pourrais obtenir le résultat que tu souhaite en modifiant un tout petit peu ton foncteur pour lui donner la forme de

                        struct Remplir{
                        public:
                         
                            int operator()(){
                                static int value{0};
                                ++value;
                                return value;
                            }
                         
                        };

                        Et en corrigeant du coup la fonction main pour lui donner la forme de

                        int main()
                        {
                            vector<int> v(100,0);
                         
                            generate(v.begin(),v.end(),Remplir{});
                            generate(v.begin(),v.begin()+10,Remplir{});
                            /* ... */
                        }

                        Mais cela t'empêcherait de définir la valeur de départ à utiliser.

                        Note que tu pourrais aussi envisager de définir m_valeur comme étant une variable statique (de préférence publique, pour pouvoir la modifier à ta guise), sous une forme proche de

                        struct Remplir{
                        public:
                            static int value;
                            int operator()(){
                                ++value;
                                return value;
                            }
                         
                        };
                        int Remplir::value = 0;

                        et l'utiliser alors sous une forme proche de

                        int main()
                        {
                            vector<int> v(100,0);
                         
                            generate(v.begin(),v.end(),Remplir{});     //value == 0
                            generate(v.begin(),v.begin()+10,Remplir{}); //value == 100
                            /* OU - OU - OU */
                            Remplir::value = 150;
                            /* utilise les valeurs allant de 150 à 165 pour les
                             * éléments à l'indice 15 à 30
                             */
                            generate(v.begin()+15, v.begin()+30, Remplir{}); 
                        }
                        • Partager sur Facebook
                        • Partager sur Twitter
                        Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                          19 août 2018 à 16:54:54

                          Qui ne saute pas aime les static ! Yeah !

                          Qui ne saute pas aime les static ! Yeah !

                          Plus serieusement, si le foncteur est copie et qu'on veut reutiliser le meme compteur, c'est peut etre que le foncteur ne devrait pas etre proprietaire du compteur.

                          #include <iostream>
                          #include <vector>
                          #include <algorithm>
                           
                          using namespace std;
                           
                           
                          class Remplir{
                          public:
                              Remplir(int& i):m_valeur(i)
                              {}
                           
                              int operator()(){
                                  ++m_valeur;
                                  return m_valeur;
                              }
                           
                          private:
                              int& m_valeur;
                          };
                           
                          int main()
                          {
                              int count { 0 };
                              Remplir foncteur(count);
                              vector<int> v(100,0);
                           
                              generate(v.begin(),v.end(),foncteur);
                              generate(v.begin(),v.begin()+10,foncteur);
                          }

                          Et voila pourquoi on n'utilise plus trop de foncteur depuis qu'on a les lambda :

                          #include <iostream>
                          #include <vector>
                          #include <algorithm>
                           
                          using namespace std;
                          
                          int main()
                          {
                              int count { 0 };
                              vector<int> v(100,0);
                           
                              generate(v.begin(),v.end(),[&count](){ return (++count); });
                              generate(v.begin(),v.begin()+10,[&count](){ return (++count); });
                          }

                          c'est quand meme plus simple.

                          • Partager sur Facebook
                          • Partager sur Twitter
                            19 août 2018 à 17:38:57

                            gbdivers a écrit:

                            <snip> c'est quand meme plus simple.

                            Aussi... Mais, vu que notre ami voulait utiliser les foncteurs, qui étaient peu ou prou la seule solution durant le paléolithique, je lui ai expliquer comment on faisait à l'époque :D

                            • Partager sur Facebook
                            • Partager sur Twitter
                            Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                              19 août 2018 à 20:13:41

                              Merci gbdivers pour ton post détaillé avec l'exemple des lambdas et Koala01 ainsi que Elried. je devrais bientôt passer à l'utilisation massive des lambdas lorsque je termine le cours d 'OC pour passer sur lambdas  massives.

                              J'apprends les foncteurs actuellement. C'est une bonne entrée en matière.

                              De plus, j'ai une question dans ton code :

                              int main()
                              {
                                  vector<int> v(100,0);
                                
                                  generate(v.begin(),v.end(),Remplir{});
                                  generate(v.begin(),v.begin()+10,Remplir{});
                                  /* ... */
                              }

                              je souhaite demande à quoi correspond la syntaxe Remplir{} que je ne connaissais pas.

                              Merci aussi à gbdivers, Elried, MichelBIllaud

                              -
                              Edité par pseudo-simple 19 août 2018 à 20:26:02

                              • Partager sur Facebook
                              • Partager sur Twitter
                                19 août 2018 à 20:55:26

                                C'est la forme -- apparue en C++11 -- que nous pourrions appeler "initialisation standardisée".

                                Avant C++11, nous avions autant de moyen de créer une variable que de catégories de donnée:

                                • on déclarait une variable de type primitif sous la forme de int i = 135;;
                                • on créait une instance de classe sous une forme proche de Maclasse obj s'il n'était pas nécessaire de fournir des argument au constructeur;
                                • on créait une instance de classe sous une forme proche deMaClasse obj(param1, param2);; si on souhaitait utiliser un constructeur paramétré;
                                • On déclarait des tableaux sous la forme de std::vector tab = {1, 2, 3, 4, 5};.
                                • (je ne suis pas sur de ne pas en oublier :p )

                                C++11 est arrivé avec la notion de "liste d'initialisateurs" (à ne pas confondre avec la liste d'initalisation du constructeur ;) ) qui nous permet d'initialiser n'importe quel type de variable de manière strictement identique : en plaçant les données nécessaires à l'initialisation entre accolade "{" et "}".

                                Nous pouvons donc désormais déclarer nos variables sous une forme qui serait proche de

                                • int i{135}; pour les types primitifs
                                • MaClasse obj{}; si on dispose d'un constructeur ne prenant aucun paramètre pour MaClasse
                                • MaClasse obj{param1, param2, param3}; si on veut utiliser un constructeur paramétré pour MaClasse et
                                • std::vector<int> tab{1, 2, 3, 4, 5}; pour créer un tableau

                                La grosse difficulté (qu'il est impossible de contourner) réside, justement, dans l'usage de std::vector (entre autres???), car cette classe dispose d'un constructeur proche de

                                explicit vector( size_type count,
                                                 const T& value = T(),
                                                 const Allocator& alloc = Allocator());

                                qui nous permet d'avoir :

                                • un tableau contenant dix élément (initialisés par défaut, et donc vallant 0) sous la forme de std::vector<int> tab(10);
                                • un tableau contenant dix élément (initialisés à 20) sous la forme de std::vector<int tab(10,20);
                                • (le dernier paramètre par défaut est tellement particulier que je n'en parlerai pas ;) )

                                Mais, dans l'absolu, cette possibilité nous offre une manière "presque unique" d'initialiser n'importe quel type de variable dés la déclaration ;)

                                • Partager sur Facebook
                                • Partager sur Twitter
                                Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                                  19 août 2018 à 21:02:46

                                  Et sinon, pourquoi ne pas apprendre avec un cours à jour ? Parce que les 3/4 de tes questions concernent la syntaxe et le fonctionnement basique de C++.

                                  Je ne sais pas comment tu t'y prends, mais ça fait un bon paquet de mois que tu nous rabâches les oreilles avec le cours d'OC, tu as l'intention de le finir un jour ?

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    19 août 2018 à 22:17:55

                                    Merci Koala01 pour cet éclaircissement. C'est très enrichissant.

                                    Merci aussi à MichelBillaud pour son aparté avec l'exemple développé en langage C. Ce fut très instructif.

                                    @jo_link_noir. Attends un peu que je termine OC, et après je ne vais plus te lâcher avec C++ moderne

                                    -
                                    Edité par pseudo-simple 19 août 2018 à 22:30:57

                                    • Partager sur Facebook
                                    • Partager sur Twitter

                                    Foncteurs C++ , pointeur sur fonction

                                    × 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