Cyanide
Cyanide

Reputation: 85

GTK+ g_pointer_connect passing data incorrectly

I have a problem with passing my data to the function when using g_signal_connect().

guint x = 777;
gpointer ptr = &x;
g_print(std::to_string(*(guint*)p).c_str());
g_signal_connect(G_OBJECT(dialogWindow), "destroy",
    G_CALLBACK(funct), ptr);

The g_print in this piece of code outputs "777". However when the funct is called

static void funct(gpointer data) {
  g_print(std::to_string(*(guint*)data).c_str());
}

the g_prints outputs some garbage like: "81382720"

Can anyone help me with this?

Upvotes: 2

Views: 531

Answers (4)

liberforce
liberforce

Reputation: 11454

You're passing a pointer to a local variable (x), and a local variable is allocated on the stack. To keep it alive outside of the scope of that function, allocate it on the heap instead (or optionally use a static or global variable).

guint *ptr = g_malloc(sizeof(guint));
*ptr = 777;
g_print(std::to_string(*ptr).c_str());
g_signal_connect(G_OBJECT(dialogWindow), "destroy",
    G_CALLBACK(funct), ptr);

Don't forget to call g_free to free that pointer when you don't need it anymore to avoid a memory leak.

EDIT:

I missed the fact that the signature of your callback is wrong as it doesn't respect the one of the destroy signal. This is a bug and must be fixed. Thanks to el.pescado for pointing that out.

Remarks on the other posts are valid too, but don't affect correctness:

  • GUINT_TO_POINTER/GPOINTER_TO_UINT can be used for that simple case to avoid dynamic allocation
  • your call to g_print is unnecessary complicated

Upvotes: 3

First, signal handlers receive by default pointer to object that received signal as first argument, and user data as last argument. So, your signal handler should have the following signature:

static void funct(GtkWidget *object, gpointer data) {
  /* ... */
}

Second, x is a local value, so it might (though not necessarily) be out of scope by the time callback is called. To fix it you could either extend its lifetime by allocating it on heap (g_new, new, malloc) or making it global/static. Alternatively, since an uint fits in pointer, you could use macros GUINT_TO_POINTER/GPOINTER_TO_UINT to store/retrieve x directly in pointer.

Last, g_print function provides formatted output like printf - instead of creating temporary std::string and extracting char pointer form it:

g_print(std::to_string(*(guint*)p).c_str());

You could simply use %u format specifier to print guint:

g_print("%u", *p);

To sum up:

guint x = 777;
g_print("%u", x);
g_signal_connect(G_OBJECT(dialogWindow), "destroy",
    G_CALLBACK(funct), GUINT_TO_POINTER(x));

// ...

static void funct(GtkWidget *object, gpointer data) {
    g_print("%u", GPOINTER_TO_UINT(data));
}

Upvotes: 2

Edouard Thiel
Edouard Thiel

Reputation: 6208

If you just want to store an integer value, use GINT_TO_POINTER() to store the value :

guint x = 777;
g_signal_connect(G_OBJECT(dialogWindow), "destroy",
    G_CALLBACK(funct), GINT_TO_POINTER(x));

and GPOINTER_TO_INT() to retrieve it:

static void funct(gpointer data) {
    guint x = GPOINTER_TO_INT(data);
}

Upvotes: 1

Licensed Slacker
Licensed Slacker

Reputation: 388

I'm suspecting that variable x is already out of scope and destroyed when callback get called when the window's "destroy" signal get called. Thus the pointer is not valid anymore when it is dereferenced.

Upvotes: 1

Related Questions