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
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
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.
Oui j'ai lu ta réponse avec le contexte du post. Mais j'avoue que je n'ai pas tout compris
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
> 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.
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 ?
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é".
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.
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.
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.
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.
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...
× 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.
Blog - GitHub
Blog - GitHub
Blog - GitHub
https://zestedesavoir.com/tutoriels/822/la-programmation-en-c-moderne/
Blog - GitHub
https://zestedesavoir.com/tutoriels/822/la-programmation-en-c-moderne/
Blog - GitHub
Blog - GitHub