Partage
  • Partager sur Facebook
  • Partager sur Twitter

istream>> pour conversion de type

Sujet résolu
    25 mai 2020 à 20:41:34

    Bonjour.

    J'ai du mal à trouver un titre pour mon problème ahah. J'ai une classe configuration qui possède un membre map<string, string>. J'essaie d'écrire une fonction template qui retourne une valeur si elle existe dans la map et selon le type template. Par exemple, si j'ajoute une pair { "foo", "1" } j'aimerai récupérer un int en faisant myfoo = config.get<int>("foo");. Pour ça j'ai écris, avec l'aide du discord C++ help, cette fonction.

    T get(const std::string &key) {
        auto iterator = m_fields.find(key);
        if (iterator != m_fields.end()) {
            std::istringstream iss(iterator->second);
            T value;
            if (iss >> value) {
                return value;
            } else {
                throw std::bad_cast();
            }
        } else {
            throw std::out_of_range("T Configuration::get() : key not found");
        }
    };

    Seulement, je me rends compte que si ça marche pour des entiers, j'ai un problème dès lors que ma donnée contient un espace. En effet si j'ai bien compris le flux est coupé par des espaces. Est-ce qu'il y aurait un moyen d'ignorer ces espaces et de ne s'arrêter qu'à la fin de la ligne ?

    Merci pour votre aide :p

    Edit : J'ai aussi essayé de faire une boucle while(iss) mais au final ça n'ajoute pas les mots dans value dans le cas d'un string mais effectue une nouvelle attribution de valeur.

    -
    Edité par Alexandre Gérault 25 mai 2020 à 20:45:36

    • Partager sur Facebook
    • Partager sur Twitter
      25 mai 2020 à 20:58:40

      Hum... J'ai comme idée que cette réponse va répondre à ta question et bien plus...

      https://openclassrooms.com/forum/sujet/retour-de-methode-virtuelle-pure-de-plusieurs-type#message-93767693

      EDIT: en deux passes, je stocke d'abord toutes les entrées trimmées à gauche dans la table associative, puis c'est au moment de l'extraction que le test de convertabilité est réalisé.

      Deux points importants ici:
      - le décodeur est spécialisable. La seule spécialisation en place est pour les chaines pour dire: c'est OK, on retourne ce qui est contenu et on ne copie rien. On pourrait rajouter d'autres spécialisations pour des types métiers (p.ex. dates juliennes...), pour des liste d'éléments, etc.
      - le cas générique vérifie que tout est consommé grâce à eof(). C'est ta question précise, mais comme indiqué: je vais bien plus loin en supportant donc flexibilité, optimisations (pas de copie des chaines, unordered_map > map), etc, et le plus important: message d'erreur explicite.

      -
      Edité par lmghs 25 mai 2020 à 21:26:52

      • Partager sur Facebook
      • Partager sur Twitter
      C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
        25 mai 2020 à 21:43:00

        Oui j'ai lu ta réponse avec le contexte du post. Mais j'avoue que je n'ai pas tout compris :ange:

        En fait, j'ai pas compris pourquoi tu as un décodeur spécial pour les strings ? Et sinon on a des approches très similaires sauf que je ne comprend pas comment fonctionne la condition de cette partie :

        template <typename T> struct decoder {
            static T decode(std::string const& text, std::string const& name) {
                std::istringstream iss(text);
                if (T value ; (iss >> value).eof()) {
                    // eof() => vérifier que le flux chaine est entièrement consommé
                    // bizarre: je n'ai pas souvenir que c'est comme ça que eof
                    // fonctionne usuellement
                    return value;
                }
                throw std::runtime_error("Cannot extract parameter "+name+
                        " as "+expected<T>::name()+" from \""+text+"\"");
            }
        };

        Ça déclare une variable de type T mais je capte pas toute la seconde partie... Pour le reste il s'agit de code (les expected) qui te permet d'afficher les exceptions si j'ai bien compris.

        Edit : Oui, moi aussi je ne faisais la conversion qu'au moment de l'extraction.

        -
        Edité par Alexandre Gérault 25 mai 2020 à 21:57:53

        • Partager sur Facebook
        • Partager sur Twitter
          25 mai 2020 à 21:57:23

          > j'ai pas compris pourquoi tu as un décodeur spécial pour les strings ?

          Je l'ai partiellement expliqué dans mon EDIT. Le deuxième point plus important est qu'il ne faut pas splitter la chaine si elle contient des espaces: il faut juste tout renvoyer.

          > je ne comprend pas comment fonctionne la condition de cette partie :

          C'est un `if(type var ; cond)` du C++17. Ici la condition est le résultat accumulé de extraction suivi de eof().

          > Pour le reste il s'agit de code (les expected) qui te permet d'afficher les exceptions si j'ai bien compris.

          En gros. Cela me sert à générer un message d'erreur pertinent. Un tel message doit contenir à mon goût: la chaine trouvée dans la config, le nom du paramètre associé, et le type que l'on a voulu extraire à partir de cette chaine. La spécialisation template autour de expected<> permet d'associer un nom de type au type C++ exact attendu.

          • Partager sur Facebook
          • Partager sur Twitter
          C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
            25 mai 2020 à 22:04:17

            Merci beaucoup pour ces informations. Dans ce cas je pense que je vais faire comme toi étant donné que ça à l'air de correspondre exactement à mon problème. Juste une dernière question : où rangez-vous les structures ? J'ai mon arborescence en deux dossiers principaux : les includes d'un côté, les sources de l'autre, avec dans chaque dossier des sous-dossiers selon les modules (core, networks, ui, etc). Mais ce sont toujours des classes. Est-ce que dans le cas où les decoders sont utiles à ma classe configuration je dois les mettre dans son header ? Ou bien je fais un decoder.cpp (que je range dans quel dossier dans ce cas ?) que j'include dans le header de configuration ?

            ├── include
            │   ├── Core
            │   │   ├── Application.hpp
            │   │   └── Configuration.hpp
            │   └── UserInterface
            │       └── Menu.hpp
            └── src
                ├── Core
                │   ├── Application.cpp
                │   └── Configuration.cpp
                └── UserInterface
                    └── Menu.cpp
            


            EDIT : Ton lien pour les bons livre C++ est mort

            -
            Edité par Alexandre Gérault 25 mai 2020 à 22:06:35

            • Partager sur Facebook
            • Partager sur Twitter
              25 mai 2020 à 22:05:19

              Salut ! Pour la seconde partie: On extrait chaque caractère du flux pour le mettre dans value jusqu'à ce qu'on trouve le caractère EOF. Comme dit dans les commentaires, istringstream.eof() "vérifie que le flux chaine est entièrement consommé".

              Trop tard... xD

              -
              Edité par Daimyo_ 25 mai 2020 à 22:06:58

              • Partager sur Facebook
              • Partager sur Twitter
                25 mai 2020 à 22:16:16

                > où rangez-vous les structures

                Sur les projets au boulot ils ont tendance à faire comme ça... Je n'aime pas du tout et ne voit pas l'intérêt à s'enquiquiner de la sorte. C'est une question de goût, je dirai.

                > EDIT : Ton lien pour les bons livre C++ est mort

                Merci pour l'info. Visiblement, ça me marche pas depuis une fenêtre où je ne suis pas identifié. Ce que je ne sais pas c'est si ça marche pour ceux qui sont identifié avec un compte google. Maintenant, ce n'est pas très grave, il ne s'agit que de vieux livres de C++98... Il va falloir que je m'occupe de cette signature un de ces quatre ^^'

                > Pour la seconde partie: On extrait chaque caractère du flux pour le mettre dans value jusqu'à ce qu'on trouve le caractère EOF. Comme dit dans les commentaires, istringstream.eof()

                Euh...non. On extrait ce qu'il est permit d'extraire et puis on vérifie que l'on est au bout. A aucun moment je ne cherche un caractère EOF.

                • Partager sur Facebook
                • Partager sur Twitter
                C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                  25 mai 2020 à 22:19:30

                  lmghs a écrit:

                  Sur les projets au boulot ils ont tendance à faire comme ça... Je n'aime pas du tout et ne voit pas l'intérêt à s'enquiquiner de la sorte. C'est une question de goût, je dirai.

                  Écrire dans le header ou un fichier séparé ? :/
                  • Partager sur Facebook
                  • Partager sur Twitter
                    25 mai 2020 à 22:29:46

                    Alexandre Gérault a écrit:

                    lmghs a écrit:

                    Sur les projets au boulot ils ont tendance à faire comme ça... Je n'aime pas du tout et ne voit pas l'intérêt à s'enquiquiner de la sorte. C'est une question de goût, je dirai.

                    Écrire dans le header ou un fichier séparé ? :/


                    Utiliser des répertoires séparés pour les headers et le sources.

                    J'ai lu trop vite pour la question précise. Désolé. Je dirai que cela dépend. Avoir 150 fichiers ne me plaît guère. Si les traits (expected<> est une "classe de traits"; et decoder<> est plus proche d'une politique (traits sous stéroïdes)) écrits ici peuvent servir ailleurs, alors définitivement, je les extrais dans leur fichiers dédiés -- au singulier en fait ici vu que c'est du template.

                    Si le traits ne sert qu'à un seul endroit, que c'est un détail interne, alors je tends à le laisser avec. Jusqu'au jour où je les extrais ailleurs ^^', ou pas. NB: les sortir au un très gros avantage: pouvoir les tester séparément.

                    • Partager sur Facebook
                    • Partager sur Twitter
                    C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                      25 mai 2020 à 22:31:42

                      Tant qu'on y est, c'est quoi un trait ?

                      EDIT: J'ai trouvé ça mais ça date de 2007... Les concepts sont toujours les mêmes ?

                      -
                      Edité par Daimyo_ 25 mai 2020 à 22:34:09

                      • Partager sur Facebook
                      • Partager sur Twitter
                        25 mai 2020 à 22:41:06

                        Oui.

                        Un trait est une information paramétrée par un paramètre template.

                        Et une politique est un comportement paramétré...

                        -
                        Edité par lmghs 25 mai 2020 à 22:42:53

                        • Partager sur Facebook
                        • Partager sur Twitter
                        C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                          25 mai 2020 à 23:56:06

                          Merci beaucoup pour ton aide :) Du coup je passe en résolu avec ce gist pour te montrer le résultat

                          Edit : Par contre je vois pas comment tester ça avec le framework Google Test :'(

                          -
                          Edité par Alexandre Gérault 26 mai 2020 à 0:43:26

                          • Partager sur Facebook
                          • Partager sur Twitter
                            26 mai 2020 à 0:46:18

                            Il manquerait quelques const (save, has).

                            make_pair n'est plus nécessaire aujourd'hui -> `{k, v}` suffit

                            <iostream>, <fstream>... coûtent cher en nombre de lignes à compiler: c'est bien de ne garder que le strict nécessaire.

                            `line.find(delimiter)` est dupliqué

                            remove() se simplifie avec juste un appel à https://en.cppreference.com/w/cpp/container/unordered_map/erase (3)

                            Côté Doxygen,
                            - les brief lines devraient être terminées par des '.'
                            - J'aime bien rajouter `[in]`, `[out]`, ou `[in,out]` sur @param
                            - > @return true if it successfully saved, false else
                              sonne bizarre. s/else/otherwise, mais sur les booléens j'aime encore mieux la concision: `@return whether the configuration was successfully saved`
                            - J'aime bien donner le max d'infos possibles sur les contrats (@pre, @post, @invariant) et sur les exceptions lancées, ou non.

                            • Partager sur Facebook
                            • Partager sur Twitter
                            C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                              26 mai 2020 à 21:32:14

                              J'ai appliqué certains changement dont les includes, les make_pair, la duplication du find, la simplification de la méthode remove et le return boolean. Par contre j'ai pas compris les précisions à propos de [in], [out] [in,out] ni les contrats...
                              • Partager sur Facebook
                              • Partager sur Twitter

                              istream>> pour conversion de type

                              × 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