Partage
  • Partager sur Facebook
  • Partager sur Twitter

Initialisation d'attributs dans une structure

    2 septembre 2018 à 15:18:16

    Bonjour à tous ! Aujourd'hui j'ai un problème un peut bizarre: Je veux initialiser une chaine dans un constructeur. Jusque là tout va bien. Mais cette chaîne est dans une structure elle même dans une autre structure !

    Or, la curiosité c'est que si je fait comme ca :

    class Test
    {
    public:
          Test(void) noexcept(true);
          ~Test(void) noexcept(true);
    private:
          struct
          {
                 int i;
                 struct
                 {
                        int i;
                        struct
                        {
                               int *ptr;
                               size_t size;
                        } three;
                 } two;
           } one;
    };
    
    ...
    
    Test(void) noexcept(true) : one({ 0, { 0, { NULL, 0} } })
    {
          ...
    }

    Tout fonctionne impeccable. Par contre si je fais ça:

    class Test
    {
    public:
          Test(void) noexcept(true);
          ~Test(void) noexcept(true);
    private:
          struct
          {
                 int i;
                 struct
                 {
                        int i;
                        struct
                        {
                               int *ptr;
                               std::string str;
                               size_t size;
                        } three;
                 } two;
           } one;
    };
    
    ...
    
    Test(void) noexcept(true) : one({ 0, { 0, { NULL, "", 0} } })
    {
          ...
    }

    Visual studio me donne un warning C26495 (https://docs.microsoft.com/fr-fr/visualstudio/code-quality/c26495?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev15.query%3FappId%3DDev15IDEF1%26l%3DFR-FR%26k%3Dk(C26495)%26rd%3Dtrue&view=vs-2017)

    Quelqu'un peut m'expliquer pourquoi et surtout comment le corrigé ?

    Merci par avance !



    -
    Edité par TomAnderson1 2 septembre 2018 à 15:22:14

    • Partager sur Facebook
    • Partager sur Twitter
      2 septembre 2018 à 15:28:10

      Alors je ne suis pas sûre, mais peut être que ton compilateur (d'ailleurs en français on dit compileur ou compilateur ?) ne considère pas "" comme une initialisation.
      • Partager sur Facebook
      • Partager sur Twitter
        2 septembre 2018 à 15:44:39

        Salut et merci de la réponse ! J'y ai penser mais c'est pas ça puisqu'il l'accepte si str est seule dans la structure.

        Essayer ce code:

        class Test
        {
        public:
        	Test(void) noexcept(true);
        	~Test(void) noexcept(true);
        private:
        	struct
        	{
        		int i;
        		struct
        		{
        			std::string str;
        			int i;
        		} two;
        
        		struct
        		{
        			int i;
        		} three;
        	} one;
        };
        
        Test::Test(void) noexcept(true) : one({ 0, { "", 0 }, { NULL, 0 } })
        {
        
        }
        
        Test::~Test(void) noexcept(true)
        {
        
        }

        Amuser vous à ajoutez et supprimer le membre i de la structure two (Et dans la liste d’initialisation). Pour ceux qui ont visual studio, avec les réglages de bases, pour voir l'erreur apparaître ou disparaître il faut utiliser l'analyse de code (Generer la solution -> Analyse de code)

        -
        Edité par TomAnderson1 2 septembre 2018 à 15:58:48

        • Partager sur Facebook
        • Partager sur Twitter
          3 septembre 2018 à 16:23:26

          Personne ? Je penser que ce genre de petit bug interesserez les gourou du C++...
          • Partager sur Facebook
          • Partager sur Twitter
            3 septembre 2018 à 17:35:09

            Ça semble être un problème dans l'analyseur.
            Pour ne plus avoir d'avertissement, on devrait pouvoir écrire :

            class Test
            {
            public:
                 Test()         // pas de noexcept car le ctor de string ne l'est pas, étonné que compilateur ne dise rien
                ~Test()noexcept(false) // un destructeur NE DOIT PAS être noexcept, étonné que le compilateur ne dise rien
            private:
                struct
                {
                    int  i  = 0;
                    struct
                    {
                        int*  ptr = nullptr;
                        std::string  str;  // une string est toujours implicitement initialisée à vide
                        int  i  = 0;
                    } two;
             
                    struct
                    {
                        int  i  = 0;
                    } three;
                } one;
            };
             
            Test::Test() // écrire void en paramètre correspond au C, et est contre-productif en C++
            {
            }
            • Partager sur Facebook
            • Partager sur Twitter

            En recherche d'emploi.

              3 septembre 2018 à 17:39:39

              NULL, c'est caca.

              Sur mon VS2017 (V15.7.3) ce code compile en /W4 avec 0 warning (le code initial ne compile même pas) :

              class Test
              {
              public:
              	Test(void) noexcept(true);
              	~Test(void) noexcept(true);
              private:
              	struct
              	{
              		int i;
              		struct
              		{
              			std::string str;
              			int i;
              		} two;
              
              		struct
              		{
              			int *ptr;
              			int i;
              		} three;
              	} one;
              };
              
              Test::Test(void) noexcept(true) : one{ 0, { "", 0 } , { nullptr, 0 } } 
              {
              
              }
              
              Test::~Test(void) noexcept(true)
              {
              
              }



              -
              Edité par bacelar 3 septembre 2018 à 17:42:42

              • Partager sur Facebook
              • Partager sur Twitter
              Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                3 septembre 2018 à 20:32:56

                Salut ! Daflad, ta méthode est un peu bourrin non ? Par contre pour les void c'est noté... Bacelar, pour le premier code ça me surprend pas, je l'ai écrit sur portable pendant mon cours de solfège. Sinon, c'est pas étonnant que t'es pas de warnings, comme je l'ai dit, tu dois utiliser l'analyse de code si tu veux les voir apparaitre (Generer->Executer l'analyse de code sur la solution)...

                PS: Quelles sont les exceptions lancées par std::string ? J'ai essayer de lire l’implémentation mais c'est beaucoup trop haut level pour moi...

                -
                Edité par TomAnderson1 3 septembre 2018 à 22:00:23

                • Partager sur Facebook
                • Partager sur Twitter
                  4 septembre 2018 à 0:48:34

                  Salut,

                  TomAnderson1 a écrit:

                  PS: Quelles sont les exceptions lancées par std::string ? J'ai essayer de lire l’implémentation mais c'est beaucoup trop haut level pour moi...

                  Oh, il y en a quelques unes :p

                  La principale pourrait être std::bad_alloc, car, il faut bien allouer la mémoire pour pouvoir contenir l'ensemble des caractères qu'elle contient  :D

                  (bon, selon les implémentations, ce n'est pas toujours vrai, en fonction de la taille de la chaine de caractères, mais bon ... passons les détails ;) )

                  Allez, lis (et essaye de comprendre) la page de doc de son constructeur... C'est édifiant :D

                  @daflab : en fait, il y a bien un constructeur de sdt::basic_string qui est noexcept  (deux, même, depuis C++17, si on compte le fait de ne pas lancer d'exception sur Allocator)  ... regarde attentivement le (1) et le (8) du lien que je viens de donner :D

                  -
                  Edité par koala01 4 septembre 2018 à 0:53:00

                  • 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
                    4 septembre 2018 à 10:56:59

                    >tu dois utiliser l'analyse de code

                    Idem, il ne me signale rien.

                    • Partager sur Facebook
                    • Partager sur Twitter
                    Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                      4 septembre 2018 à 14:51:42

                      TomAnderson1 a écrit:

                      Personne ? Je penser que ce genre de petit bug interesserez les gourou du C++...

                      Les gourous du C++ savent comment éviter ce genre de code, ils ne savent pas comment regler ce genre de bug :D

                      Plus sérieusement, il s'agit d'un warning de l'analyseur. Ca arrive souvent que les analyseurs utilisent des heuristiques différentes de la compilation, justement pour essayer de trouver des problèmes qui ne seraient pas trouvés par la compilation. En plus, c'est un warning pour les C++ Core Guidelines, donc une vérification optionnelle.

                      Il faut vérifier les warnings des analyseurs, mais ce n'est pas catastrophique s'il y a de tels warnings.

                           Test()         // pas de noexcept car le ctor de string ne l'est pas, étonné que compilateur ne dise rien
                          ~Test()noexcept(false) // un destructeur NE DOIT PAS être noexcept, étonné que le compilateur ne dise rien
                      

                      Les garanties apportées par "noexcept" ne sont pas très fortes, ce n'est généralement pas vérifié par les compilateurs. (Il y a des tonnes de discussions sur le sujet)

                      Par contre, le destructeur devrait etre noexcept. Cf https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f6-if-your-function-may-not-throw-declare-it-noexcept 

                      Note
                      Destructors, swap functions, move operations, and default constructors should never throw.

                      (pour les constructeurs par défaut, voir aussi C.44)

                      • Partager sur Facebook
                      • Partager sur Twitter
                        4 septembre 2018 à 16:26:58

                        gbdivers a écrit:

                        Par contre, le destructeur devrait etre noexcept. Cf https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f6-if-your-function-may-not-throw-declare-it-noexcept 

                        Note
                        Destructors, swap functions, move operations, and default constructors should never throw.

                        (pour les constructeurs par défaut, voir aussi C.44)

                        Je suis pas sûr de bien avoir saisi... Mais je le comprend comme "Quitte à plus savoir quoi faire, je cours vers la sortie sans réfléchir aux problèmes que je vais causer". C'est pas encore pire ?

                        (Enfin ceci dit, j'ai cru lire que tout les destructeurs sont noexcept par défaut (en supposant que ce soit commun à tout les compilateurs ?), la question ne se pose même pas dans ce cas là)



                        -
                        Edité par Sillimon 4 septembre 2018 à 16:36:27

                        • Partager sur Facebook
                        • Partager sur Twitter
                          4 septembre 2018 à 16:32:57

                          Sillimon a écrit:

                          Je suis pas sûr de bien avoir saisi...

                          Je suis sur que tu n'as pas bien saisi :D

                          La règle "les destructeurs ne devraient pas émettre d'exception" ne datent pas de "noexcept". 

                          • Partager sur Facebook
                          • Partager sur Twitter
                            4 septembre 2018 à 16:58:25

                            Il y a des raisons pour dire qu'une fonction devrait noexcept:

                            • destructeur: il n'est pas possible de correctement attraper une exception lancé par un destructeur. De plus, s'il y a déjà une exception lancée et que le destructeur en lance une autre, le programme va crasher. Actuellement, il n'y a pas de mécanisme d'exception imbriquée. Et comme le destructeur par défaut est en noexcept(true), lancer une exception va résulter par un std::abort.
                            • swap: parce qu'utilise 1 constructeur par déplacement et 1 opérateur = de déplacement.
                            • move-ctor, move-assignment: les conteneurs utilisent ces fonctions uniquement si elles sont noexcept. Dans le cas contraire, c'est les versions par copie qui seront utilisées. Cela permet de garantir qu'un déplacement des éléments n'arrête pas l'algorithme en cours de route et de toujours avoir les données dans le conteneur en cas d'exception (vector::resize par exemple). Et comme un déplacement n'altère pas réellement les données, il ne devrait jamais y avoir d'exception jetée.
                            • constructeur par défaut: Je suis mitigé sur celui-là. Mais la plupart du temps il n'y a pas de gestion de ressource et l'instance est soit vide, soit pas encore initialisé (destructible mais pas utilisable sans ajouter des "éléments"). Après, on peut se retrouver avec des ouvertures de fichier et la justification serait que le ctor par défaut n'a pas à les ouvrir s'il ne reçoit pas les chemins en paramètre.

                            -
                            Edité par jo_link_noir 4 septembre 2018 à 16:59:36

                            • Partager sur Facebook
                            • Partager sur Twitter
                              4 septembre 2018 à 17:23:40

                              Heu... Merci de toute ces réponses ! Mais les dernières dérivent un peu... Si j'ai bien compris, j'ignore ce warnings ? Sinon comment l'éviter ?
                              • Partager sur Facebook
                              • Partager sur Twitter
                                4 septembre 2018 à 17:58:16

                                "If an exception is not supposed to be thrown, the program cannot be assumed to cope with the error and should be terminated as soon as possible. Declaring a function noexcept helps optimizers by reducing the number of alternative execution paths. It also speeds up the exit after failure."

                                C'est très possible mais j'ai bloqué là-dessus :
                                 > If an exception is not supposed to be thrown

                                Comment est-ce que tu juges de ça ? Je vois tout le monde dire "Ah non c'est interdit !!" moi je suis d'accord... mais j'aimerais savoir pourquoi. >_<

                                Les infos que j'ai :

                                • Sur les destructeur : ça correspondait pas trop mal à ma description
                                  --> "it's not clear what should happen if an exception is thrown from a destructor during the process of UnwindingTheStack. C++ defines a harsh rule, which says that an exception escaping from a destructor during stack unwinding will cause program termination, and that's not what you want"
                                  Comprendre, "On ne sait pas le gérer (ou c'est bien trop chiant) donc on provoque un hard crash en espérant limiter les dégâts" ? Parce que si noexcept call terminate(), on peut potentiellement penser que ce process d'unwinding n'est pas fini, nan ?

                                • swap, move OK. Je trouve ça plutôt logique.

                                • Celui-ci j'ai du mal --> "Being able to set a value to "the default" without operations that might fail simplifies error handling and reasoning about move operations".
                                  Par.... simplicité ? praticité ?

                                EDIT :

                                jo_link_noir a écrit:

                                Il y a des raisons pour dire qu'une fonction devrait noexcept:

                                [...]

                                OK merci ! C'est les infos que je voulais trouver/confirmer.

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  4 septembre 2018 à 18:12:42

                                  Les warnings des analyseurs doivent etre etudiées au cas par cas et ne sont pas toujours pertinent. C'est le cas ici, les membres sont correctement initialisés. C'est probablement un bug de l'analyseur (ou un faux positif)

                                  jo_link_noir a écrit:

                                  • destructeur: il n'est pas possible de correctement attraper une exception lancé par un destructeur. De plus, s'il y a déjà une exception lancée et que le destructeur en lance une autre, le programme va crasher. Actuellement, il n'y a pas de mécanisme d'exception imbriquée. Et comme le destructeur par défaut est en noexcept(true), lancer une exception va résulter par un std::abort.

                                  Et surtout, quel sens donner a une exception dans un destructeur ? Ca veut dire que toutes les ressources ne sont pas libérée ? Aucune ressource ? Comment récupérer la gestion des ressources dans ce cas ? Quel traitement convient ?

                                  jo_link_noir a écrit:

                                  • constructeur par défaut: Je suis mitigé sur celui-là. Mais la plupart du temps il n'y a pas de gestion de ressource et l'instance est soit vide, soit pas encore initialisé (destructible mais pas utilisable sans ajouter des "éléments"). Après, on peut se retrouver avec des ouvertures de fichier et la justification serait que le ctor par défaut n'a pas à les ouvrir s'il ne reçoit pas les chemins en paramètre.

                                  L'utilisation de noexecpt avec les constructeurs par defaut est contreversé. Mais le propos est bien de ne rien faire qui pourrait lancer une exception, en particulier allouer des ressources o_O (Oui... on peut se demander l'utilité du constructeur dans ce cas...) Cf C.44 du C++ Core Guideline.

                                  En fait, c'est lié aux objets "partiellement formés" a une certaine conception de la sémantique de valeur. Merci Stepanov. Cf https://www.developpez.net/forums/d1678999/c-cpp/cpp/langage/apprendre-construction-classes-cpp-types-objets-stepanov/ et l'article lié.

                                  Mon point de vue : ne pas faire d'objets "partiellement formés". Jamais. Et si on le fait, on ne l'avoue pas.

                                  -
                                  Edité par gbdivers 4 septembre 2018 à 18:13:42

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    4 septembre 2018 à 18:16:41

                                    Salut,

                                    Si j'ai bien compris, tu te retrouve avec un avertissement "C26495 MEMBER_UNINIT" qui te dit -- en gros -- qu'il y a une donnée membre qui n'est pas initialisée.

                                    Le problème, c'est que tu travailles sur des structures imbriquées et anonymes, ce qui ne facilite vraiment pas la tâche quand il s'agit de vérifier si c'est l'analyseur de code qui "perd les pédales" ou si c'est effectivement toi qui a "fait une gourde".

                                    Les deux premières questions que j'aurais envie de te poser sont donc sans doute totalement idiotes, mais:

                                    • Pourquoi  des structure imbriquées (dans des structures  imbriquées pour la plupart d'entre-elles)?
                                    • Pourquoi des structure anonyme?

                                    En un mot comme en cent : ne pourrais tu pas travailler de manière "plus classique", sous une forme qui serait peut-être proche de

                                    struct V{
                                        V(int * ptr, int size)noexcept(true):
                                            ptr{ptr}, size{size}{
                                        }
                                        int *ptr;
                                        size_t size;
                                    };
                                    struct U{
                                        U(int i, int * ptr, int size)noexcept(true):
                                            i{i},tree{ptr, size}{
                                        }
                                        int i;
                                        V tree;
                                    };
                                    struct T{
                                        T(int i, int j, int * ptr, int size)noexcept(true):
                                          i{i},two{j, ptr, size}{
                                        }
                                        int i;
                                        U two;
                                    }
                                    class Test{
                                    public:
                                        Test()noexcept(true):one{0,0,nullptr,0}{
                                        }
                                    private:
                                       T one;
                                    };

                                     Ne serait-ce pas d'une certaine manière beaucoup plus simple (et, très sûrement plus compréhensible pour l'analyse)???

                                    -
                                    Edité par koala01 4 septembre 2018 à 18:17:28

                                    • 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
                                      5 septembre 2018 à 12:57:06

                                      Koaka01, y a un truc que j'aurais peut-être dû préciser: mon langage d'origine, c'est le C. Pas le C++. Et en C, les structures sont un moyen de créer des variables et de ranger son code. Pas des classes avec une visibilité publique par défaut. Moi, j'utilise ces structures pour ranger des variables et leurs propriétés (chaînes de characteres, longueur, ...). Voilà pourquoi je me retrouve avec des structures anonymes et imbriquées. Tu comprendras donc que je sois surpris par ton code... Quelle est donc la meilleure manière de m'organiser avec un style C++ ?

                                      PS: Le fait que les constructeurs des objets standards comme std::string lancent des exceptions signifie que ces objets ne doivent surtout pas être déclaré en variable globable (je sais, c'est mal mais hypothéquon) n'est-ce pas ? Ne serait-i pas plus simple pour remonter les erreurs de passer une référence aux constructeurs ? Comme un int qui prendrait les valeurs d'errno ?

                                      -
                                      Edité par TomAnderson1 5 septembre 2018 à 18:20:02

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        14 septembre 2018 à 0:44:36

                                        Les histoires de visibilité sont un faux problème. Pour qu'un programme fonctionne correctement, il y a une condition nécessaire (mais non suffisante), qui est que toutes les variables doivent être initialisée avant d'être utilisées.  Tout programme qui ne respecte pas strictement cette règle est faux par nature.

                                        Si tu veux faire les choses bien en C, tu vas faire une fonction initializeStruct(struct *) et une fonction releaseStruct(struct *) qui correspondent exactement au constructeur et au destructeur de c++ à la différence que tu devras les appeler explicitement alors que constructeur et destructeur sont appelés implicitement par c++ ce qui évite d'oublier de les appeler ^^ 

                                        De l'intérêt d'une exception par rapport à errno... L'exception remonte la pile d'appel, je la capte là où je suis en mesure de la traiter, avant ça ne sert à rien, je ne sais pas comment la traiter, après, c'est trop tard le mal est fait. Résultat, j'ai un code de traitement d'erreur parfaitement identifié et localisé au point où je capture l'exception, je sais quelle erreur je traite et surtout je sais comment je vais la traiter (sinon je ne capture pas, ça ne sert à rien). Avec un vague paramètre errno (ou une valeur de retour), il faut la traiter ou la retourner à chaque étape, si je l'ignore, la catastrophe n'est pas loin, si je la sur traite, j'alourdis mon code pour des nèfles, quoi que je fasse, je suis sub optimal. Il existe un autre gag avec un errno, c'est le multithread, si une exception se produit, pas de soucis, elle remonte la pile du thread sur laquelle elle se produit, avec un errno, il peut y avoir une commutation de thread entre le moment où l'erreur se produit et celui où tu vas récupérer le code. Si jamais un autre thread a provoqué une erreur, tu l'as dans l'os, l'erreur que tu vas remonter peut éventuellement être celle de l'autre thread, et évidemment il est fort peu probable que tu sois en mesure de la traiter correctement...

                                        • 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
                                          15 septembre 2018 à 19:34:44

                                          Je croyais qu'errno était threads-safe et que c'était précisément pour cette raison qu'elle était implémenter sous forme de macro ?
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            15 septembre 2018 à 20:03:41

                                            TomAnderson1 a écrit:

                                            Je croyais qu'errno était threads-safe et que c'était précisément pour cette raison qu'elle était implémenter sous forme de macro ?


                                            Comme dirait mon père: "laisses croires les béguines, elles sont spécialistes et payées pour" :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
                                              15 septembre 2018 à 20:46:20

                                              errno est normalement qualifié thread_local depuis c++11. Il n'y a normalement pas de conflit entre thread.

                                              • Partager sur Facebook
                                              • Partager sur Twitter

                                              Initialisation d'attributs dans une structure

                                              × 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