Forum Programmation.c Glib et les Binary Trees

Posté par  (site web personnel) .
Étiquettes : aucune
0
28
juin
2007
Bonjour à tous,

j'essaie d'utiliser les fonctions B-Tree qu'offre Glib mais je rencontre quelques petits soucis.
En fait, j'ajoute des valeurs à la chaine dans un B-Tree et quand je lookup ces valeurs, elles ont disparues... seule reste la dernière ajoutée...

voilà mon code :

#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

GTree * redirected_connections;

int main(int argc, char **argv)
{
redirected_connections = g_tree_new((GCompareDataFunc)strcmp);

int i;
for (i=1; i < 250; i++)
{
int value = (i * i);

gchar intkey[4];
snprintf(intkey,4, "%d", i);

gchar intval[6];
snprintf(intval,6, "%d", value);

fprintf(stdout, "add %s:%s\n",intkey, intval );
g_tree_insert(redirected_connections, intkey, intval);
}

for (i=1;i<250;i++)
{
gchar intkey[4];
snprintf(intkey,4, "%d", i);

fprintf(stdout, "check value %s:%s\n", intkey, g_tree_lookup(redirected_connections, intkey));
}

gint numberofnodes = g_tree_nnodes(redirected_connections);

fprintf(stdout, "# of nodes : %d\n",numberofnodes);

g_tree_destroy (redirected_connections);
}


Il se compile chez moi avec la ligne
gcc -Wall -o test_b-tree test_b-tree.c `pkg-config --cflags --libs glib-2.0`


Je ne comprend pas pourquoi il m'écrase toutes les autres valeurs, il devrait ajouter une feuille à l'arbre à chaque fois et le ré-équilibrer... au lieu de celà, on dirait qu'il écrase l'arbre

J'ai fait le même type de code avec les Hash Tables de Glib et ça marche parfaitement...

Une idée d'où vient mon erreur ?
  • # normal

    Posté par  (site web personnel) . Évalué à 3.

    btree ne fait qu'indexer des zones émoires...

    ici tu ne fais ton traitement que sur le même pointeur!

    tu devrais soit faire un malloc pour chaque element ou alors faire un tableau!
    • [^] # Re: normal

      Posté par  (site web personnel) . Évalué à 1.

      Un tableau de pointeur ou chaque feuille de l'arbre serait une case du tableau ?
      Je dois avouer que je suis un peu pommé, n'étant pas particulièrement développeur (enfin, comme tout le monde quoi)

      je me suis rabattu sur les hash table, plus simples à utiliser et qui me semblent bien rapides pour ce que je fait... mais je doit avouer que le sentiment face au B-Tree m'irrite :p
      et sur le net, pas d'exemple, que des liens vers la doc officielle...

      Donc au final, si t'avais un exemple de code, ca m'intéresse ;)
      • [^] # Re: normal

        Posté par  . Évalué à 3.

        Sache que t'es pas obligé de mettre des chaines de caractère dans un gtree,
        cependant voici un exemple pour que ton application fonctionne :

        #include <glib.h>
        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>

        GTree * redirected_connections;

        int main(int argc, char **argv)
        {
        redirected_connections = g_tree_new_full((GCompareDataFunc)strcmp,NULL,(GDestroyNotify)free,(GDestroyNotify)free);

        int i;
        for (i=1; i < 250; i++)
        {
        int value = (i * i);

        gchar* intkey = (gchar*)malloc(sizeof(gchar)*4);
        snprintf(intkey,4, "%d", i);

        gchar* intval = (gchar*)malloc(sizeof(gchar)*6);
        snprintf(intval,6, "%d", value);

        fprintf(stdout, "add %s:%s\n",intkey, intval );
        g_tree_insert(redirected_connections, intkey, intval);
        }

        for (i=1;i<250;i++)
        {
        gchar intkey[4];
        snprintf(intkey,4, "%d", i);

        fprintf(stdout, "check value %s:%s\n", intkey, g_tree_lookup(redirected_connections, intkey));
        }

        gint numberofnodes = g_tree_nnodes(redirected_connections);

        fprintf(stdout, "# of nodes : %d\n",numberofnodes);

        g_tree_destroy (redirected_connections);
        }
      • [^] # Re: normal

        Posté par  (site web personnel) . Évalué à 3.

        Ça n'a rien à voir avec les B-Tree. Tu donnes toujours l'adresse du même emplacement mémoire à placer dans le B-Tree, quel que soit le noeud: intkey et intval. Ils sont créés dans la pile alors que tu devrais les créer dans le tas (donc avec un truc qui *alloue* de la mémoire) ! En plus dès que tu sors de ton "for", tes tableaux n'existent déjà plus (demande à ton prof des détails sur la visibilité d'une variable)!

        Ce sera vrai avec des GNode, des GSList, des GList, etc... C'est à toi de créer la mémoire qui stocke tes données, le boulot des GTree, GNode, et. c'est juste de pouvoir retrouver cette mémoire.

        Donc commence par installer devhelp qui t'aidera à trouver les fonctions que tu veux.
        Ensuite, si tu remplaces tes tableaux de gchar par des gchar * que tu fais pointer à de la mémoire alloué sur le tas (heap) et pas sur la pile (stack) comme tu le faisais, bin ça marchera.


        Ainsi par exemple:

        gchar intkey[4];
        snprintf(intkey,4, "%d", i);

        Devient:

        gchar *intkey;
        intkey = g_strdup_printf("%d", i);


        N'oublie pas que cette mémoire allouée, tu dois dois la désallouer quand tu n'en as plus besoin, pour éviter les "célèbres" fuites mémoire (memory leak). Utilise g_free pour cela.
        • [^] # Re: normal

          Posté par  . Évalué à 2.

          Pas con le coup du g_strdup_printf, c'est plus conci que le malloc que j'ai proposé...

          Pour la désallocation, dans mon exemple, je lui dit d'utiliser g_tree_new_all qui permet de spécifier le destructeur pour la clé et le contenu, du coup c'est automatique, il n'aura pas besoin de désallouer à la mano.
          par contre faudra remplacer free par g_free ...
          • [^] # Re: normal

            Posté par  (site web personnel) . Évalué à 3.

            Mouaip, sachant que là il crée des objets qui n'ont pas tous la même taille (chaine de caratère dont à priori on ne connait pas la taille à l'avance). Si par contre il crée de nombreuses fois le même objet, une structure par exemple, alors il vaut mieux se pencher sur les memory slices, plus performants en terme d'allocation, désallocation: http://developer.gnome.org/doc/API/2.0/glib/glib-Memory-Slic(...)
            • [^] # Re: normal

              Posté par  (site web personnel) . Évalué à 1.

              Que dire... si ce n'est un grand merci pour ces explications claires et précises ! :)
              • [^] # Re: normal

                Posté par  (site web personnel) . Évalué à 2.

                De rien :-)
                Ah, si tu veux encore plus utiliser la glib, avec les facilités qu'elle propose, tu peux remplacer les fprintf(stdout,...) par g_print par exemple.

                Je ne peux que t'enjoindre à lire la documentation de la glib, dont les opérations sur les chaines de caractères, qui fourmillent de fonctions plus agréables que celles de la lib standard du C:
                http://developer.gnome.org/doc/API/2.0/glib/glib-String-Util(...)
                • [^] # Re: normal

                  Posté par  (site web personnel) . Évalué à 1.

                  J'ai, effectivement, commencé à utiliser la Glib de plus en plus.
                  En partie grâce au livre "C en action" de Yves Mettier, très pratique pour les codeux du dimanche comme moi... (formation secu info, pas du tout dev)

                  Le soucis, c'est les deadlines qui m'oblige à passer des étapes régulièrement et donc m'empêche de prendre le temps de réapprendre (encore qu'il y ais plus à apprendre qu'a ré-apprendre :p).

                  Bref, le B-Tree se remplit correctement maintenant, mais pour le nettoyer, si j'ai bien compris, il faut parcourir les branches et feuilles avec "traverse", lister dans un tableau de pointeur (GPtrArray ?) les keys que l'on veut supprimer pour, dans un second temps, appeler "remove" sur ces clés

                  j'ai bon ?
                  • [^] # Re: normal

                    Posté par  . Évalué à 2.

                    non, comme je l'ais dit, tu peux faire appel à g_tree_new_full(),
                    cette fonction te crée un b-tree vide, tu lui donne en paramètre les fonctions chargées de désallouer la mémoire pour toi.
                    pour désallouer la mémoire d'une chaine de caractère, tu fait appel à g_free(memoire_a_desallouer). donc pour qu'il te désalloue la mémoire allouée pour des chaines de caractère pour la clé et les données, tu fais :

                    g_tree_new_full(strcmp,NULL,g_free,g_free);

                    quand tu fera appel à g_tree_destroy(), pour chaque noeuds il videra la mémoire pour toi.
                    • [^] # Re: normal

                      Posté par  (site web personnel) . Évalué à 0.

                      oui mais en fait non :p
                      je souhaite supprimer des entrées du tableau selon un critère sur la valeur de l'entrée

                      je l'ai fait, et ca marche, il faut parcourir le tableau avec g_tree_traverse qui appel une fonction pour chaque valeur
                      dans cette fonction, j'enregistre la clé si mon critère match
                      et une fois que g_tree_traverse est terminé, je supprime les valeurs matchées

                      il semblerait que ca fonctionne proprement ;)
                      • [^] # Re: normal

                        Posté par  . Évalué à 3.

                        gni? comment ça, non?

                        Tu fais appel à quoi? si tu fait appel à g_tree_remove, ça fait aussi appel tout seul aux fonctions de desallocation mémoire si tu utilise g_tree_new_full() pour créer ton arbre.
                        d'ailleurs c'est explicitement écrit dans la doc des g_tree...

                        donc, qu'est-ce qui est faux dans ce que j'ai dit?
                        • [^] # Re: normal

                          Posté par  (site web personnel) . Évalué à 1.

                          Oui oui ce que tu as dit est juste, mais c'était pas tout à fait ma question en fait :p

                          J'ai besoin de traverser l'arbre pour tester des valeurs et supprimer des feuilles en fonctions de ces valeurs.
                          Donc oui j'utilise g_tree_remove qui appel les destructeurs listés dans g_tree_new_full, mais je doit faire cela APRES avoir parcouru l'arbre en entier

                          ze purpose of my question, c'était de savoir si j'utilisais la bonne méthode ;)

                          j'utilise les 3 fonctions suivantes :



                          /*! watchman for the B-Tree, wake up every 5 minutes and check every entries */
                          void clean_table()
                          {
                          while (1)
                          {
                          /*! wake up every 5 minutes */
                          sleep(300);

                          /*! init pointer table */
                          entrytoclean = g_ptr_array_new();

                          /*! call the clean function for each value, delete the value if TRUE is returned*/
                          g_tree_traverse( redirected_connections,(GHRFunc) match_old_value, G_IN_ORDER, NULL );

                          /*! remove each key listed from the btree */
                          g_ptr_array_foreach(entrytoclean,(GFunc) remove_old_value, NULL);

                          /*! free the array */
                          g_ptr_array_free(entrytoclean, TRUE);
                          }
                          }



                          /*! called for each entry in the B-Tree, if a time value is upper to 5 minutes, entry is deleted */
                          int match_old_value(gpointer key, gint value, gpointer trash)
                          {
                          struct tms actual;

                          gint *valtime;
                          valtime = g_strdup_printf("%d", times(&actual));

                          if( (valtime - value) > 30000 )
                          {
                          g_ptr_array_add(entrytoclean, key);
                          }
                          return FALSE;
                          }



                          /*! called for each entry in the pointer array, each entry is a key that is deleted from the B-Tree */
                          void remove_old_value(gpointer key, gpointer trash)
                          {
                          g_print("CLEANER => %s removed\n",key);
                          if (TRUE != g_tree_remove(redirected_connections, key))
                          {
                          g_print("error while removing %s /!\ KEY NOT FOUND IN B-TREE /!\\n", key);
                          }
                          }

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.