IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Boost.Any et std::map


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre du Club
    Inscrit en
    Mai 2010
    Messages
    6
    Détails du profil
    Informations forums :
    Inscription : Mai 2010
    Messages : 6
    Par défaut Boost.Any et std::map
    Bonjour à tous,

    J'ai une petite question toute bête... pour ceux qui savent

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    std::map<std::string, boost::any> session;
    session["string"] = "azerty";
    session["int"] = 1;
     
    std::string s = boost::any_cast<std::string>(Session["string"]);
    int i = boost::any_cast<int>(Session["int"]);
    Jusque là, tout va bien

    Maintenant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    session["map"] = std::map<std::string, boost::any>();
     
    session["map"]["1"] = "1"; // ne fonctionne pas, comment faire simplement ?
    Jean-Michel

  2. #2
    Invité
    Invité(e)
    Par défaut
    slt,

    si tu fais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     int  i = boost::any_cast<int>(Session["int"]);
    alors tu devrais aussi faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    boost::any_cast<std::map<std::string, boost::any> >(session["map"])
    Ensuite, tu essaies de modifier une propriété de la map (ce qui est différent quand tu fais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     int i = boost::any_cast<int>(Session["int"]);
    ) parce que tu fais qu'une lecture.

    Si tu regardes la doc de any_cast , tu vois que pour modifier il faut passer un pointeur.

    Donc quelquechose du style
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    #include <iostream>
    #include <boost/any.hpp>
    #include <map>
    int main()
    {
      std::map<int, boost::any> amap;
      typedef std::map<int, int> MapType;
      amap[1]=MapType();
      amap[2]=MapType();
      {
        MapType* amapTemp = boost::any_cast<MapType>(&amap[1]);//pointeur pour modifier
        (*amapTemp)[3]=2;
      }
      {
        MapType amapTemp = boost::any_cast<MapType>(amap[2]);//copie, amap non modifiee
        amapTemp[0]=1;
      }
      std::cout << "modified: "<< boost::any_cast<MapType>(amap[1])[3] <<std::endl;
      std::cout << "not modified: "<< boost::any_cast<MapType>(amap[2])[0] <<std::endl;
      return 0;
    }

  3. #3
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    En un mot comme en cent : il ne faut jamais utiliser l'opérateur [ ] avec une map!!!

    Alors, je sais... Certains diront qu'une std::map est un tableau associatif permettant de faire le lien entre une clé et une valeur, et que, à partir du moment où c'est un tableau, l'accès se fait par l'opérateur []. Seulement, ils se gourent lourdement.

    En effet, la sémantique de l'opérateur [] est telle que l'on s'attend à accéder à un élément existant de la collection sur laquelle on l'applique.

    Lorsque l'on écrit monTableau[10] on s'attend simplement à accéder au onzième élément de monTableau, que l'élément soit constant ou non.

    Or, l'opérateur [] de std::map fait bien plus que de simplement donner l'accès à l'élément identifié par la clé :si la clé n'existe pas, il crée la clé et lui donne la valeur correspondant au constructeur par défaut (ne prenant pas d'argument).

    Et c'est là tout le problème! On va régulièrement faire des recherches sur la clé pour déterminer si la valeur associée existe (a été créée ) ou non.

    Si on commence à faire recherce la recherche avec find à certains endroits et à utiliser des tests proches de if( lamap[cle]== lavaleur) à d'autres, find va renvoyer un élément, qui ne correspond pas à lamap.end(), mais qui n'est pas sensé exister et dont la valeur a "simplement" été créée par défaut.

    En outre, il faut se méfier comme de la peste d'un code qui pourrait avoir (sans doute dans différentes fonctions) un résultat proche de ce que l'on obtiendrait avec le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    std::map<std::string, std::string> lamap;
    lamap["salut"] = "salut";
    /* ... */
    lamap["salut"] = "world";
    car la valeur serait alors la dernière valeur ainsi définie ("world" au lieu de "salut" qui était voulue)

    Pour toutes ces raisons, on recherche un élément dans la map avec find( clé), et on compare le résultat de cette recherche avec le résultat de la fonction membre end() (qui indique un élément connu pour être inexistant) et, pour l'insertion, on utilise la fonction insert (ou emplace, si on a besoin de la sémantique de mouvement), dont on récupère le résultat afin de tester le second élément (insert renvoie une pair<iterator, bool> si besoin :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    int main(){
        std::map<std::string, std::string> lamap;
        std::string key("salut");
        std::string values[2]={ "salut",
                                "hello"};
        for(size_t index = 0;index<2;++index){
            bool inserted=lamap.insert(std::make_pair(key, values[index])).second;
            // OU   -   OU   -   OU
            // bool inserted=lamap.emplace(std::make_pair(key, values[index])).second;
            if(inserted)
                std::cout<<"inserted key "<<key <<"with value "
                         <<values[index]<<std::endl;
            else
            std::cout<<"not inserted key, allready exists with value "
                     <<lamap.find(key)->second<<std::endl
                     <<"value "<<values[index]<<" skipped"<<std::endl;
     
        }
        return 0;
    }
    Ceci étant dit, cela ne résoud sans doute pas ton problème (quoi que... :-D).

    Alors, la première chose que je remarque, c'est que tu définis session comme étant de type std::map<std::string, boost::any> et que tu essayerais de donner comme valeur pour "map" quelque chose qui serait ... une std::map<std::string,any>

    C'est peut être ce que tu veux, mais cela veut dire que tu essayes de créer une arborescence qui pourrait ressembler à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    map :
        subMap1:
            "value1" = "salut"
            "value2" = 3
            ...
        subMap2
           subSubMap1 :
               "value1" = 5
               "value2" = "hello"
          subSubMap2:
                "object" = untruc
                "value2" = "world"
    Je n'ai rien contre l'idée, mais est-ce vraiment ce que tu souhaites faire

    Si c'est bien ce que tu souhaites faire, je te conseillerais volontiers de travailler de manière récursive.

    Cela pourrait prendre la forme d'une fonction proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    std::map<std::string, boost::any> fillTree(/* ... */){
        std::map<std:string, boost::any> recup;
        while(sameLevel) { // il faut veiller à ce qu'on ne retourne pas au niveau "parent" (*)
            if(itemReadisLeaf) { // le cas de base : l'élément rajouté est une "feuille" (**)
                recup.insert(std::make_pair(key, itemRead));
            else
                /* sinon, on insère le résultat de l'appel de la fonction (***) */
               recup.insert(std::make_pair(key, fillTree(/* ... */ )));
        }
        return recup;
    }
    (*) sameLevel correspond à une vérification qui te permet de t'assurer que tu ne t'apprêtes pas à créer un frêre du noeud que tu es justement occupé à créer

    (**) Le meilleur moyen de traiter la récursivité, c'est de traiter le "cas de base" (ou, si tu préfères, le cas qui permet de ne pas rappeler la fonction une fois de plus).

    Dans le cas présent, le cas de base correspond au fait que tu as rajouté une "feuille" (un élément qui n'a pas d'enfants).

    Il t'appartient de trouver comment déterminer si l'élément ajouté avait des enfants ou non

    (***) Si on n'est pas dans le cas de base, c'est que l'élément que l'on veut rajouter est un noeud (un élément qui est lui-même composé d'enfants).

    On fait donc appel à la fonction pour entrer dans la récursivité
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #4
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Hello,

    Pourquoi utiliser boost::any et non pas boost::variant ?

    On connait le type quand on stocke la valeur, et on en à besoin en la récupérant (ou au minimum une liste de types possibles pour le pattern visiteur).

    On peut arriver au même résultat :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    using namespace std;
     
    enum Type { T_INT, T_FLOAT, T_STRING };
     
    struct Data;
    struct Loop;
     
    typedef boost::variant<Data, Loop> variant_type;
    typedef boost::variant<int, float, string> data_type;
     
    struct Data {
    	explicit Data(int d): type(T_INT), data(d) { }
    	explicit Data(float d): type(T_FLOAT), data(d) { }
    	explicit Data(const string& d): type(T_STRING), data(d) { }
     
    	Type type;
    	data_type data;
    };
     
    struct Loop { map<string, variant_type> loopData; };
     
    void print(Data& data) {
    	switch(data.type) {
    	case T_INT: cout << "int: "; break;
    	case T_FLOAT: cout << "float: "; break;
    	case T_STRING: cout << "string: "; break;
    	default:
    		break;
    	}
    	cout << data.data << endl;
    }
     
    Data getData(const Loop& l, const initializer_list<string> where, string name) {
    // Data getData(const Loop& l, const initializer_list<string>& where, string name) {
    // donne :
    // fatal error C1001: An internal error has occurred in the compiler.
    // 1>  (compiler file 'msc1.cpp', line 1453)
    	const Loop *pl = &l;
     
    	for(const auto& str: where) {
    		pl = &boost::get<Loop>(pl->loopData.find(str)->second);
    	}
    	return boost::get<Data>(pl->loopData.find(name)->second);
    }
     
    int main() {
    	Loop m0;
     	m0.loopData.insert(make_pair("1", Data(1)));
    	m0.loopData.insert(make_pair("2", Data(2.f)));
    	m0.loopData.insert(make_pair("3", Data("3")));
     
    	Loop m1;
    	m1.loopData.insert(make_pair("4", Data(4)));
    	m1.loopData.insert(make_pair("5", Data(5.f)));
    	m1.loopData.insert(make_pair("6", Data("6")));
     
    	Loop m0_0;
    	m0_0.loopData.insert(make_pair("7", Data(7)));
    	m0_0.loopData.insert(make_pair("8", Data(8.f)));
    	m0_0.loopData.insert(make_pair("9", Data("9")));
     
    	m0.loopData.insert(make_pair("m0_0", m0_0));
     
    	Loop l;
     	l.loopData.insert(make_pair("m0", m0));
     	l.loopData.insert(make_pair("m1", m1));
     
    	auto d = getData(l, {"m0", "m0_0"}, "7");
    	print(d);
     
    	return 0;
    }
    Qui génèrerrait le graphe suivant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    l
        "m0":
            "1": 1
            "2": 2.0f
            "3": "3"
            "m0_0": 
                "7": 7
                "8": 8.0f
                "9": "9"
        "m1":
            "4": 4
            "5": 5.0f
            "6": "6"
    Mais aussi, si le but est de représenter des sessions pour un serveur Web (le nom de session me fait penser à ça, peut être que tu bosses sur quelque chose qui n'a rien à voir^^) il n'y à pas besoin de récursivité infinie.
    Le but étant plus d'accéder à une valeur particulière par sa clef que d’effectuer un traitement sur toutes les valeurs d'un certain type (pattern visiteur).

    Et quelque chose de basique pourrait être suffisant ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    enum Type { T_INT, T_FLOAT, T_STRING };
     
    struct Data {
    	Type type;
    	int intValue;
    	float floatValue;
    	string stringValue;
    	/* ... */
    };
     
    map<string, map<string, Data>> sessions;

Discussions similaires

  1. std::map::find
    Par Blowih dans le forum SL & STL
    Réponses: 12
    Dernier message: 21/12/2005, 19h42
  2. Accession aux std::map triées ?
    Par Matthieu Brucher dans le forum SL & STL
    Réponses: 5
    Dernier message: 18/11/2005, 14h44
  3. std::map<int,CStringArray> ????
    Par philippe V dans le forum MFC
    Réponses: 1
    Dernier message: 12/10/2005, 06h48
  4. Libérer des pointeurs dans une std::map
    Par GaldorSP dans le forum SL & STL
    Réponses: 2
    Dernier message: 09/07/2005, 14h42
  5. Trier un std::map selon les valeurs plutot que les clés
    Par dj.motte dans le forum SL & STL
    Réponses: 2
    Dernier message: 13/11/2004, 21h54

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo