GTK+ Forums Forum Index GTK+ Forums
Discussion forum for GTK+ and Programming. Ask questions, troubleshoot problems, view and post example code, or express your opinions.
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Modifying a GList in a callback

 
Post new topic   Reply to topic    GTK+ Forums Forum Index -> GTK+ Example Code
Author Message
Ossi
GTK+ Geek


Joined: 09 Sep 2008
Posts: 67
Location: Denmark

PostPosted: Mon Jan 19, 2009 8:05 pm    Post subject: Modifying a GList in a callback Reply with quote

"Example" part is in post #2, here -dreblen
Hi all.

I am playing around with some GLists. In the code shown below I make MAXSTRUCT structs and prepend them to a GList. Every time i click the button a element from the list should be copied/prepended to another list, and the element from the first list should be deleted.

Somehow the code does not work, the first time i click the button everything works fine. But the second time i click the button the list length is 1. I can not figure out where it goes wrong. Hopefully somebody here can help me.

Code: (C)
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <gtk/gtk.h>
#include <stdlib.h>

#define MAXSTRUCT (100)

GtkWidget *label;

typedef struct STestStruct
{
    gint x;
    gchar buf[1024];
}TestStruct;

GList *initList(void)
{
    gint i;
    GList *list;
   
    /*Make MAXSTRUCT test struct*/
   
TestStruct *test = (TestStruct *)calloc(MAXSTRUCT, sizeof(TestStruct));
   
    /*prepend the test structs to list*/
   
for(i = 0; i < MAXSTRUCT; i++)
    {
        if(i == 0)
            list = g_list_prepend(NULL, &test[i]);
        else
           
list = g_list_prepend(list, &test[i]);
    }
       
    /*Just to make sure that length = MAXSTRUCT*/   
   
g_message("g_list_length(list) = %i\n", g_list_length(list));
   
    return list;
}

/*Everytime the button is clicked the data from the first element in list, should be
  copied/prepended to another list. Then the first element from the first list should be
  deleted.*/
void buttonClicked(GtkWidget *widget, GList *list)
{
    static GList *ptr;
    gchar *str;
   
    g_message("list length = %i", g_list_length(list));
    ptr = g_list_first(list);
   
    /*Some rutine to copy the ptr->data to another list*/
   
    /*Delete the element from the list*/
   
list = g_list_delete_link(list, ptr);
   
    /*Show the remaining list elements*/
   
str = g_strdup_printf("%i", g_list_length(list));
    gtk_label_set_text(GTK_LABEL(label), str);
    g_free(str);
}

int main (int argc, char **argv)
{
    GtkWidget *window;
    GtkWidget *button;
   
    GtkWidget *hbox;
    gchar *str;

    GList *test = initList();

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
   

    /* Add your widgets to the window here. */
   
hbox = gtk_hbox_new(FALSE, 5);
    gtk_container_add(GTK_CONTAINER(window), hbox);
   
    button = gtk_button_new_with_label("sub. from list");
    g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(buttonClicked), (gpointer)test);
    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 2);
   
    str = g_strdup_printf("%i", g_list_length(test));
    label = gtk_label_new(str);
    gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 2);
    g_free(str);
   
    gtk_widget_show_all (window);
    /* Run the main loop. */
   
gtk_main ();

    return 0;
}
Back to top
tadeboro
Never Seen the Sunlight


Joined: 23 Jul 2008
Posts: 2114
Location: Slovenia

PostPosted: Mon Jan 19, 2009 11:06 pm    Post subject: Reply with quote

Hello.

This is one of those hard-to-explain pointer mangling errors. But I'll do my best to explain.

First we need to clear some things. When we use functions that manipulate GList, we usually pass them in a pointer to the first element, and get first element's (new) address as a return value. For example, let's have a look at this function call:
Code: (C)
1
2
GList *test;
test = g_list_delete_link( test, test );

If ASCII-art below this paragraph represents 3-element GList in memory, where numbers mean memory addresses, and we want to remove first element in list, we pass test as a first and second parameter to the g_list_delete_link. g_list_delete_link function receives address 120, removes first element and returns address 150, which we save back into test. This way we still have valid pointer in test variable.
Code: (Plaintext)
1
2
3
4
5
6
7
100        .-> 120         .-> 150         .-> 160
 +------+  |   ^ +------+  |   ^ +------+  |     +------+
 | test-|--´   | | data |  |   | | data |  |     | data |
 +------+      | | next-|--´   | | next-|--´     | next |
               | | prev |  .-----|-prev |  .-----|-prev |
               | +------+  |   | +------+  |     +------+
               `-----------´   `-----------´   


Similarly, when you pass test as a parameter to the g_signal_connect function, you're actually sending in memory address 120 (the address of the firs GList element. The callback function can then operate on GList, but it cannot report results of the operations back to the main loop and so our test variable suddenly points to invalid location.

The solution to this problem is to pass "pointer to pointer to first element" as a parameter. See another of my "high resolution" ASCII graphics for visual representation of "pointer to pointer" mumbo-jumbo (tmp is a pointer to pointer). This way we are passing in location of the first element's address and this provides a way to communicate back without the return value. We simply write GList's new starting address to the location pointed by the parameter passed to the callback function.

Code: (Plaintext)
1
2
3
4
5
6
7
350        .-> 100         .-> 120         .-> 150         .-> 160
  +-----+  |     +------+  |   ^ +------+  |   ^ +------+  |     +------+
  | tmp-|--´     | test-|--´   | | data |  |   | | data |  |     | data |
  +-----+        +------+      | | next-|--´   | | next-|--´     | next |
                               | | prev |  .-----|-prev |  .-----|-prev |
                               | +------+  |   | +------+  |     +------+
                               `-----------´   `-----------´   


You're probably feeling dizzy right now from all this pointer talk, but unfortunately English is not my "home" language and this is the best I can give. You may benefit more from modified code sample, which incorporates the principle described above. If you need more explanation, just ask, but I cannot guarantee that my next explanation will be any better that this one.

Code: (C)
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <gtk/gtk.h>
#include <stdlib.h>

#define MAXSTRUCT (100)

GtkWidget *label;

typedef struct STestStruct
{
    gint x;
    gchar buf[1024];
}TestStruct;

GList *initList(void)
{
    gint i;
    GList *list;
   
    /*Make MAXSTRUCT test struct*/
   
TestStruct *test = (TestStruct *)calloc(MAXSTRUCT, sizeof(TestStruct));
   
    /*prepend the test structs to list*/
   
for(i = 0; i < MAXSTRUCT; i++)
    {
        if(i == 0)
            list = g_list_prepend(NULL, &test[i]);
        else
           
list = g_list_prepend(list, &test[i]);
    }
       
    /*Just to make sure that length = MAXSTRUCT*/   
   
g_message("g_list_length(list) = %i\n", g_list_length(list));
   
    return list;
}

/*Everytime the button is clicked the data from the first element in list, should be
  copied/prepended to another list. Then the first element from the first list should be
  deleted.*/
void buttonClicked(GtkWidget *widget, GList **list)
{
    gchar *str;
   
    g_message("list length = %i", g_list_length(*list));
   
    /*Some rutine to copy the ptr->data to another list*/
   
    /*Delete the element from the list*/
   
*list = g_list_delete_link(*list, *list);
   
    /*Show the remaining list elements*/
   
str = g_strdup_printf("%i", g_list_length(*list));
    gtk_label_set_text(GTK_LABEL(label), str);
    g_free(str);
}

int main (int argc, char **argv)
{
    GtkWidget *window;
    GtkWidget *button;
   
    GtkWidget *hbox;
    gchar *str;

    GList  *tmp = initList();
    GList **test = &tmp;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
   

    /* Add your widgets to the window here. */
   
hbox = gtk_hbox_new(FALSE, 5);
    gtk_container_add(GTK_CONTAINER(window), hbox);
   
    button = gtk_button_new_with_label("sub. from list");
    g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(buttonClicked), (gpointer)test);
    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 2);
   
    str = g_strdup_printf("%i", g_list_length(*test));
    label = gtk_label_new(str);
    gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 2);
    g_free(str);
   
    gtk_widget_show_all (window);
    /* Run the main loop. */
   
gtk_main ();

    return 0;
}
Back to top
Ossi
GTK+ Geek


Joined: 09 Sep 2008
Posts: 67
Location: Denmark

PostPosted: Tue Jan 20, 2009 2:38 pm    Post subject: Reply with quote

Thx a lot tadeboro!

This helped me a lot!!

Maybe this small tutorial should be moved to GTK+ Example Code section ?
Back to top
dreblen
Never Seen the Sunlight


Joined: 14 Jun 2007
Posts: 936
Location: Falun, WI USA

PostPosted: Tue Jan 20, 2009 8:39 pm    Post subject: Reply with quote

Moved. I also changed the title to something more appropriate.
Back to top
Display posts from previous:   
Post new topic   Reply to topic    GTK+ Forums Forum Index -> GTK+ Example Code All times are GMT
Page 1 of 1

 


Powered by phpBB © 2001, 2005 phpBB Group
CodeBB 1.0 Beta 2
Protected by Anti-Spam ACP